Get .NET Core JsonSerializer to serialize private members - c#

I have a class with a private List<T> property which I would like to serialize/deserialize using the JsonSerializer. Use of the JsonPropertyAttribute doesn't seem to be supported in .NET Core. So how can I have my private list property serialized?
I'm using System.Text.Json for this.

It seems System.Text.Json does not support private property serialization.
https://learn.microsoft.com/tr-tr/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#internal-and-private-property-setters-and-getters
But as the Microsoft's document says, you can do it with custom converters.
https://www.thinktecture.com/en/asp-net/aspnet-core-3-0-custom-jsonconverter-for-the-new-system_text_json/
Code snippet for serialization & deserialization;
public class Category
{
public Category(List<string> names)
{
this.Names1 = names;
}
private List<string> Names1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
}
public class CategoryJsonConverter : JsonConverter<Category>
{
public override Category Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var name = reader.GetString();
var source = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(name);
var category = new Category(null);
var categoryType = category.GetType();
var categoryProps = categoryType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in source.Keys)
{
var categoryProp = categoryProps.FirstOrDefault(x => x.Name == s);
if (categoryProp != null)
{
var value = JsonSerializer.Deserialize(source[s].GetRawText(), categoryProp.PropertyType);
categoryType.InvokeMember(categoryProp.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance,
null,
category,
new object[] { value });
}
}
return category;
}
public override void Write(Utf8JsonWriter writer,
Category value,
JsonSerializerOptions options)
{
var props = value.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.ToDictionary(x => x.Name, x => x.GetValue(value));
var ser = JsonSerializer.Serialize(props);
writer.WriteStringValue(ser);
}
}
static void Main(string[] args)
{
Category category = new Category(new List<string>() { "1" });
category.Name2 = "2";
category.Name3 = "3";
var opt = new JsonSerializerOptions
{
Converters = { new CategoryJsonConverter() },
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json = JsonSerializer.Serialize(category, opt);
var obj = JsonSerializer.Deserialize<Category>(json, opt);
Console.WriteLine(json);
Console.ReadKey();
}
Result;
"{\"Names1\":[\"1\"],\"Name2\":\"2\",\"Name3\":\"3\"}"

System.Text.Json supports private property serialization starting with .NET 5 according to Micosoft documentation.
System.Text.Json supports private and internal property setters and getters via the [JsonInclude] attribute. - source
Find more details here.

Although you cannot serialize a private field directly as is, you can do it indirectly.
You need to provide a public property for the field and a constructor as in the following example:
class MyNumbers
{
// This private field will not be serialized
private List<int> _numbers;
// This public property will be serialized
public IEnumerable<int> Numbers => _numbers;
// The serialized property will be recovered with this dedicated constructor
// upon deserialization. Type and name must be the same as the public property.
public MyNumbers(IEnumerable<int> Numbers = null)
{
_numbers = Numbers as List<int> ?? Numbers?.ToList() ?? new();
}
}
The following code demonstrates how that works:
string json;
// Serialization
{
MyNumbers myNumbers = new(new List<int> { 10, 20, 30});
json = JsonSerializer.Serialize(myNumbers);
Console.WriteLine(json);
}
// Deserialization
{
var myNumbers2 = JsonSerializer.Deserialize<MyNumbers>(json);
foreach (var number in myNumbers2.Numbers)
Console.Write(number + " ");
}
Output:
{"Numbers":[10,20,30]}
10 20 30
If you want to detract people from accessing your private data, you can change the name to something explicitly forbidden like __private_numbers.
class MyNumbers2
{
private List<int> _numbers;
public IEnumerable<int> __private_numbers => _numbers;
public MyNumbers2(IEnumerable<int> __private_numbers = null)
{
_numbers = __private_numbers as List<int> ?? __private_numbers?.ToList() ?? new();
}
}
If an external coder is fool enough to access that private data as if it was part of the normal programming interface of that class, then shame on him. You are in your plain right to change that "private interface" without any guilt. And he can't mess with your internal list either, with an IEnumerable.
In most situations, that should be enough.

Related

C# | Create a json string out of static class fields with values

I need a method that will be able to loop through every static property in a static class, and combine these to a string with json syntax, where key name would equal to Property name, and key value would be the value of the property.
So the result would be a string with value:
{ "StaticPropertyName_string": "string_value_of_this_property", "StaticPropertyName_int": 34 }
I have a working method that successfully does the opposite - takes a json and maps data to static fields. But can't figure out how to do it in reverse
public static void MapProfileToJson(JToken source) //// source = plain json string
{
var destinationProperties = typeof(Fields)
.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (JProperty prop in source)
{
var destinationProp = destinationProperties
.SingleOrDefault(p => p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
var value = ((JValue)prop.Value).Value;
if (typeof(Fields).GetProperty(prop.Name) != null)
destinationProp.SetValue(null, Convert.ChangeType(value, destinationProp.PropertyType));
else
Console.WriteLine("(!) property \"" + prop.Name + "\" is not found in class... skip");
}
}
Example static class:
public static class Fields
{
public static bool BoolField { get; set; }
public static string StringField { get; set; }
public static int IntField { get; set; }
public static double DoubleField { get; set; }
}
p.s.
Key value pairs saved in string, could be wrapped at the end like
ResultString = "{ " + resultString + " }";
C# .NET 4.7.2 (console app)
As others have said, a static class is not a good design here.
However, it is possible to map it back to JSON with some simple reflection:
public static JObject MapStaticClassToJson(Type staticClassToMap)
{
var result = new JObject();
var properties = staticClassToMap.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in properties)
{
result.Add(new JProperty(prop.Name, prop.GetValue(null, null)));
}
return result;
}
I think it would be nice to have some simple code to assign json properties to a static class properties too. I created this code using #RichardDeeming answer
public static void MapStaticClassFromJson(string json, Type staticClassToMap)
{
var jsonObject = JObject.Parse(json);
var properties = staticClassToMap.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in properties)
prop.SetValue(null, Convert.ChangeType(jsonObject[prop.Name], prop.PropertyType, CultureInfo.CurrentCulture), null);
}
test
MapStaticClassFromJson(json,typeof(Data));
Console.WriteLine(Data.StaticPropertyName_int);
Console.WriteLine(Data.StaticPropertyName_string);
result
34
string_value_of_this_property

.NET core 3: Order of serialization for JsonPropertyName (System.Text.Json.Serialization)

While migrating to .NET Core 3 I've switched from Newtonsoft.Json serialization to System.Text.Json.Serialization. Of all the features I want to continue using JsonPropertyName attribute.
Newtonsoft version allowed ordering of serialized attributes:
[JsonProperty(Order = 1)]
public bool Deleted { get; set; }
[JsonProperty(Order = 2)]
public DateTime DeletedDate { get; set; }
Is there a way to achieve the same in System.Text.Json.Serialization?
While this feature is not implemented in .NET Core, we can apply desired ordering by creating a custom JsonConverter. There are a few ways how that can be achievable. Below is the implementation I've came up with.
Explanation - the JsonPropertyOrderConverter handles the types having at least one property with a custom order value applied. For each of those types, it creates and caches a sorter function that converts an original object into an ExpandoObject with the properties set in a specific order. ExpandoObject maintains the order of properties, so it can be passed back to JsonSerializer for further serialization. The converter also respects JsonPropertyNameAttribute and JsonPropertyOrderAttribute attributes applied to serializing properties.
Please note that Sorter functions deal with PropertyInfo objects that can add some extra latency. If the performance is critical in your scenario, consider implementing Function<object, object> sorter based on Expression trees.
class Program
{
static void Main(string[] args)
{
var test = new Test { Bar = 1, Baz = 2, Foo = 3 };
// Add JsonPropertyOrderConverter to enable ordering
var opts = new JsonSerializerOptions();
opts.Converters.Add(new JsonPropertyOrderConverter());
var serialized = JsonSerializer.Serialize(test, opts);
// Outputs: {"Bar":1,"Baz":2,"Foo":3}
Console.WriteLine(serialized);
}
}
class Test
{
[JsonPropertyOrder(1)]
public int Foo { get; set; }
[JsonPropertyOrder(-1)]
public int Bar { get; set; }
// Default order is 0
public int Baz { get; set; }
}
/// <summary>
/// Sets a custom serialization order for a property.
/// The default value is 0.
/// </summary>
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
sealed class JsonPropertyOrderAttribute : Attribute
{
public int Order { get; }
public JsonPropertyOrderAttribute(int order)
{
Order = order;
}
}
/// <summary>
/// For Serialization only.
/// Emits properties in the specified order.
/// </summary>
class JsonPropertyOrderConverter : JsonConverter<object>
{
delegate ExpandoObject SorterFunc(object value, bool ignoreNullValues);
private static readonly ConcurrentDictionary<Type, SorterFunc> _sorters
= new ConcurrentDictionary<Type, SorterFunc>();
public override bool CanConvert(Type typeToConvert)
{
// Converter will not run if there is no custom order applied
var sorter = _sorters.GetOrAdd(typeToConvert, CreateSorter);
return sorter != null;
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
// Resolve the sorter.
// It must exist here (see CanConvert).
var sorter = _sorters.GetOrAdd(value.GetType(), CreateSorter);
// Convert value to an ExpandoObject
// with a certain property order
var sortedValue = sorter(value, options.IgnoreNullValues);
// Serialize the ExpandoObject
JsonSerializer.Serialize(writer, (IDictionary<string, object>)sortedValue, options);
}
private SorterFunc CreateSorter(Type type)
{
// Get type properties ordered according to JsonPropertyOrder value
var sortedProperties = type
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.GetCustomAttribute<JsonIgnoreAttribute>(true) == null)
.Select(x => new
{
Info = x,
Name = x.GetCustomAttribute<JsonPropertyNameAttribute>(true)?.Name ?? x.Name,
Order = x.GetCustomAttribute<JsonPropertyOrderAttribute>(true)?.Order ?? 0,
IsExtensionData = x.GetCustomAttribute<JsonExtensionDataAttribute>(true) != null
})
.OrderBy(x => x.Order)
.ToList();
// If all properties have the same order,
// there is no sense in explicit sorting
if (!sortedProperties.Any(x => x.Order != 0))
{
return null;
}
// Return a function assigning property values
// to an ExpandoObject in a specified order
return new SorterFunc((src, ignoreNullValues) =>
{
IDictionary<string, object> dst = new ExpandoObject();
var isExtensionDataProcessed = false;
foreach (var prop in sortedProperties)
{
var propValue = prop.Info.GetValue(src);
if (prop.IsExtensionData)
{
if (propValue is IDictionary extensionData)
{
if (isExtensionDataProcessed)
{
throw new InvalidOperationException($"The type '{src.GetType().FullName}' cannot have more than one property that has the attribute '{typeof(JsonExtensionDataAttribute).FullName}'.");
}
foreach (DictionaryEntry entry in extensionData)
{
dst.Add((string)entry.Key, entry.Value);
}
}
isExtensionDataProcessed = true;
}
else if (!ignoreNullValues || !(propValue is null))
{
dst.Add(prop.Name, propValue);
}
}
return (ExpandoObject)dst;
});
}
}
It's supported in .Net 6 and greater using JsonPropertyOrderAttribute:
JsonPropertyOrderAttribute Class
Specifies the property order that is present in the JSON when serializing. Lower values are serialized first. If the attribute is not specified, the default value is 0.
If multiple properties have the same value, the ordering is undefined between them.
The attribute can be applied e.g. as follows:
[JsonPropertyOrder(order : 1)]
I ended up having a 2-pass approach. First pass is my normal json serializer with all converters, pocos, etc. 2nd pass is a "normalizer" to deal with whitespace/indenting/property order/etc.
There are so many corner cases trying to do this with a converter in a single pass. Properties aren't just via reflection, they can be hidden in:
Dictionaries
[JsonExtensionData] attributes
JSonElement
other converters!
It's very challenging to write a converter that deals with all of these. So I went with the 2-pass approach. The 2nd pass just operates on JsonElement and a json writer, and so avoids all the corner cases.
(we're using this in production at: https://github.com/microsoft/PowerApps-Language-Tooling/blob/master/src/PAModel/Utility/JsonNormalizer.cs )
// Write out Json in a normalized sorted order.
// Orders properties, whitespace/indenting, etc.
internal class JsonNormalizer
{
public static string Normalize(string jsonStr)
{
using (JsonDocument doc = JsonDocument.Parse(jsonStr))
{
return Normalize(doc.RootElement);
} // free up array pool rent
}
public static string Normalize(JsonElement je)
{
var ms = new MemoryStream();
JsonWriterOptions opts = new JsonWriterOptions
{
Indented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
using (var writer = new Utf8JsonWriter(ms, opts))
{
Write(je, writer);
}
var bytes = ms.ToArray();
var str = Encoding.UTF8.GetString(bytes);
return str;
}
private static void Write(JsonElement je, Utf8JsonWriter writer)
{
switch(je.ValueKind)
{
case JsonValueKind.Object:
writer.WriteStartObject();
// !!! This is where we can order the properties.
foreach (JsonProperty x in je.EnumerateObject().OrderBy(prop => prop.Name))
{
writer.WritePropertyName(x.Name);
Write(x.Value, writer);
}
writer.WriteEndObject();
break;
// When normalizing... original msapp arrays can be in any order...
case JsonValueKind.Array:
writer.WriteStartArray();
foreach(JsonElement x in je.EnumerateArray())
{
Write(x, writer);
}
writer.WriteEndArray();
break;
case JsonValueKind.Number:
writer.WriteNumberValue(je.GetDouble());
break;
case JsonValueKind.String:
// Escape the string
writer.WriteStringValue(je.GetString());
break;
case JsonValueKind.Null:
writer.WriteNullValue();
break;
case JsonValueKind.True:
writer.WriteBooleanValue(true);
break;
case JsonValueKind.False:
writer.WriteBooleanValue(false);
break;
default:
throw new NotImplementedException($"Kind: {je.ValueKind}");
}
}
}
I think the answers here all help with the 'issue'... Here's my custom solution that has been working for me.
JsonPropertyOrderAttribute spot in the #AndreyCh answer.
Adding here as well:
/// <summary>
/// Orders a property to be in a specific order when serailizing
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class JsonPropertyOrderAttribute : JsonAttribute
{
public JsonPropertyOrderAttribute(int order)
{
Order = order;
}
public int Order { get; }
}
But this is my converter... handling the 'reads' as well has allowed me to make it a 'global' converter in my JsonSerializerOptions.
public class JsonPropertyOrderConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert) =>
typeToConvert.GetProperties().Any(x => x.GetCustomAttribute<JsonPropertyOrderAttribute>(true) != null);
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var newOptions = new JsonSerializerOptions(options);
if (newOptions.Converters.Contains(this))
{
newOptions.Converters.Remove(this);
}
return JsonSerializer.Deserialize(ref reader, typeToConvert, newOptions);
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
var orderedProperites = value.GetType().GetProperties()
.Where(x => x.GetCustomAttribute<JsonIgnoreAttribute>(true) == null)
.Select(x => new
{
Info = x,
Order = x.GetCustomAttribute<JsonPropertyOrderAttribute>(true)?.Order ?? 0
})
.OrderBy(x => x.Order)
.Select(x => x.Info);
var work = new Dictionary<string, object>();
foreach (var property in orderedProperites)
{
if (property.PropertyType.IsClass)
{
var propValue = property.GetValue(value, null);
if (propValue == null && options.IgnoreNullValues)
{
//do nothing
}
else
{
var classObj = JsonSerializer.Deserialize<object>(JsonSerializer.Serialize(propValue, options));
var jsonPropertyName = property.GetCustomAttribute<JsonPropertyNameAttribute>(true)?.Name;
if (!string.IsNullOrEmpty(jsonPropertyName))
work[jsonPropertyName] = classObj;
else
work[options.PropertyNamingPolicy?.ConvertName(property.Name) ?? property.Name] = classObj;
}
}
else
{
var propValue = property.GetValue(value, null);
if (propValue == null && options.IgnoreNullValues)
{
//do nothing
}
else
{
var jsonPropertyName = property.GetCustomAttribute<JsonPropertyNameAttribute>(true)?.Name;
if (!string.IsNullOrEmpty(jsonPropertyName))
work[jsonPropertyName] = propValue;
else
work[options.PropertyNamingPolicy?.ConvertName(property.Name) ?? property.Name] = propValue;
}
}
}
var newValue = JsonSerializer.Deserialize<object>(JsonSerializer.Serialize(work));
JsonSerializer.Serialize(writer, newValue, options);
}
}

Get class properties in C# (whitout instantiating it)

I've a class "TradingStrategy", with n subclasses ("Strategy1, Strategy2 etc...").
I've a simple UI from which i can choose a subclass (I've got all the subclasses of the "TradingStrategy" class pretty easily).
What i want now is to print (in a datagridview, listbox, combobox, doesn't matter) all the public parameters of the choosen subclass.
I would prefer not to instantiate the subclasses.
namespace BackTester
{
class TradingStrategy
{
public string Name;
}
class MA_Test : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 12;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test()
{
}
}
class MA_Test2 : TradingStrategy
{
new public string Name = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
public int len = 24;
public float lots = 0.1F;
public bool trendFollow = true;
public MA_Test2()
{
}
}
}
With this code i can insert into a combo box every subclass of "TradingStrategy"
var type = typeof(TradingStrategy);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (var t in types){
if (t.Name == "TradingStrategy") continue;
boxStrategy.Items.Add(t.Name);
}
I wanna be able to, from the combobox.Text, get all the properties name and values of the corrisponding subclass.
I think I've read (and tried) every post here and in other forum. Many use reflections.
What is the simplest way to get those prop/values?
Thanks
Why not just create an interface ITradingStrategy:
public interface ITradingStrategy
{
string Name { get; }
int len { get; }
float lots { get; }
bool trendFollow { get; }
}
And have all classes inherit from the interface then pull values from interface.
As was mentioned in the comments, you have to instantiate an instance of the class in order to set some values on it.
To get the public fields/properties and their types without instantiating the objects, you can use reflection as follows:
private static Dictionary<string, Type> GetFields(Type t)
{
var fields = new Dictionary<string, Type>();
foreach (var memberInfo in t.GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
fields.Add(propertyInfo.Name, propertyInfo.PropertyType);
}
if (fieldInfo != null)
{
fields.Add(fieldInfo.Name, fieldInfo.FieldType);
}
}
return fields;
}
If you already have the object, you can get all the public fields/values with this method.
private static Dictionary<string, object> GetValues(FileInfo o)
{
var values = new Dictionary<string, object>();
foreach (var memberInfo in o.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public))
{
var propertyInfo = memberInfo as PropertyInfo;
var fieldInfo = memberInfo as FieldInfo;
if (propertyInfo != null)
{
values.Add(propertyInfo.Name, propertyInfo.GetValue(o, null));
}
if (fieldInfo != null)
{
values.Add(fieldInfo.Name, fieldInfo.GetValue(o));
}
}
return values;
}
The following code is a very slow way to get all the types which derive from a given type, due to the way that the CLR implements GetTypes() and the fact there could be thousands of unrelated types in your code which makes the haystack to search even bigger. The only time you should use this method is if you dynamically load assemblies at runtime containing object definitions that you need to load. Unfortunately there is no other way to get this information at runtime:
var type = typeof(TradingStrategy);
var subtypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p != type && type.IsAssignableFrom(p));
I would recommend that you store this list of types somewhere in your code, e.g. in an array, and iterate over it when you need to know all of your strategies:
private static readonly Type[] TradingStrategies =
{
typeof(Strategy1),
typeof(Strategy2),
typeof(Strategy3),
};
After reading Erik's answer. If you will never instantiate these classes, you could store this data in a configuration file, and use something like JSON.net to read it, or if you don't want to use an external library, XmlSerializer would work as well. In this case you would store each MATest as a Dictionary (which lends itself nicely to JSON.net's JObject. Using JSON.net, you would have a configuration file that looks like:
[
{
"MA_Test": {
"len": 12,
"lots": 0.1,
"trendFollow": true
},
"MA_Test2": {
"len": 24,
"lots": 0.1,
"trendFollow": true
}
}
]
Then read it with code that looks like:
public JObject ReadConfig(string configPath)
{
using (var filestream = File.Open(configPath, FileMode.Open))
using (var streamReader = new StreamReader(filestream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var jsonSerializer = new JsonSerializer();
return jsonSerializer.Deserialize<JObject>(jsonTextReader);
}
}
Thank you all for you answers.
The simplest way I found to get the properties from an indirected instantiated class is this:
var strategy = activator.CreateInstance(Type.GetType("BackTester."+boxStrategy.Text));
foreach (FieldInfo prop in strategy.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance))
{
listBox1.Items.Add(prop.ToString() + " " + prop.GetValue(strategy));
}
Based on the code you've provided, there is no reason for there to be separate classes for each MA_Test (X DO NOT use underscores, hyphens, or any other nonalphanumeric characters.). Instead these should be the same class with different properties (not fields).
class TradingStrategy
{
public string Name { get; set; }
}
class MATest : TradingStrategy
{
// this is not needed if it is inherited by TradingStragegy
// You should be getting a warning that you are hiding
// the field/property
// public string Name { get; set; }
// Should probably be more descriptive
// e.g. LengthInFeet...
public int Length { get; set; }
public float Lots { get; set; }
// I recommended boolean properties to be prefixed with
// Is, Can, Has, etc
public bool CanTrendFollow { get; set; }
}
// Somewhere Else...
var MATests = new List<MATest>()
{
new MATest()
{
Name = "MATest",
Length = 12,
Lots = 0.1F,
CanTrendFollow = true
},
new MATest()
{
Name = "MATest",
Length = 24,
Lots = 0.1F,
CanTrendFollow = true
},
}
Now instead of costly Reflection and Activator, just create the list classes once (manually, from config or even a database), and they can be used for whatever you need.

JSON.Net: Force serialization of all private fields and all fields in sub-classes

I have a class with several different classes and I send the information in these classes out to clients but I don't want to send them all out so some are private, some have the [JsonObject(MemberSerialization.OptIn)] flag etc.
However, now I want to do a backup of all these objects when I need to shutdown the server and every 12 hours (I don't want to use a database) so what I want to do (if possible) is to force the JSON.Net Serializer to convert the object and all the object belonging to that object.
For example:
class Foo
{
public int Number;
private string name;
private PrivateObject po = new PrivateObject();
public string ToJSON()
{ /* Serialize my public field, my property and the object PrivateObject */ }
}
I tried this code (even though it's obsolete) but it doesn't Serialize the objects related to my object:
Newtonsoft.Json.JsonSerializerSettings jss = new Newtonsoft.Json.JsonSerializerSettings();
Newtonsoft.Json.Serialization.DefaultContractResolver dcr = new Newtonsoft.Json.Serialization.DefaultContractResolver();
dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
jss.ContractResolver = dcr;
return Newtonsoft.Json.JsonConvert.SerializeObject(this, jss);
This should work:
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
var json = JsonConvert.SerializeObject(obj, settings);
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
#L.B's answer is great. But ... it requires .NET 3.5 or above.
For those of us stuck with 2.0 ...
public class ForceJSONSerializePrivatesResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
List<Newtonsoft.Json.Serialization.JsonProperty> jsonProps = new List<Newtonsoft.Json.Serialization.JsonProperty>();
foreach( var prop in props )
{
jsonProps.Add( base.CreateProperty(prop, memberSerialization));
}
foreach( var field in type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) )
{
jsonProps.Add ( base.CreateProperty( field, memberSerialization ) );
}
jsonProps.ForEach(p => { p.Writable = true; p.Readable = true; });
return jsonProps;
}
}
...seems to work.
Awesome thanks #L.B. Here's a full implementation in a .linq script in case anyone wants to test with private subclasses - e.g. See A has private subclass B.
void Main()
{
var a = A.Test();
SerialiseAllFields.Dump(a);
}
class A
{
private int PrivField1;
private int PrivProp1 { get; set; }
private B PrivSubClassField1;
public static A Test()
{
return new A { PrivField1 = 1, PrivProp1 = 2, PrivSubClassField1 = B.Test() };
}
}
class B
{
private int PrivField1;
private int PrivProp1 { get; set; }
public static B Test()
{
return new B { PrivField1 = 3, PrivProp1 = 4 };
}
}
// Define other methods and classes here
public static class SerialiseAllFields
{
public static void Dump(object o, bool indented = true)
{
var settings = new Newtonsoft.Json.JsonSerializerSettings() { ContractResolver = new AllFieldsContractResolver() };
if (indented)
{
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
}
Newtonsoft.Json.JsonConvert.SerializeObject(o, settings).Dump();
}
}
public class AllFieldsContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
var props = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
The interesting thing is that the backing fields for the properties are also serialized i.e. output is:
{
"PrivProp1": 2,
"PrivField1": 1,
"<PrivProp1>k__BackingField": 2,
"PrivSubClassField1": {
"PrivProp1": 4,
"PrivField1": 3,
"<PrivProp1>k__BackingField": 4
}
}
this is modified version of the previous accepted answer, this will also serialize private fields/properties. performance a bit increased. (serialization without BinaryFormatter a bit slower)
public class CloneContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type,
MemberSerialization memberSerialization)
{
List<MemberInfo> members = GetSerializableMembers(type);
if (members == null)
throw new JsonSerializationException("Null collection of serializable members returned.");
members.AddRange(type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => !f.CustomAttributes.Any(x => x.AttributeType
== typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute))));
members.AddRange(type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => !f.CustomAttributes.Any(x => x.AttributeType
== typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute))));
JsonPropertyCollection properties = new JsonPropertyCollection(type);
members.ForEach(member =>
{
JsonProperty property = CreateProperty(member, memberSerialization);
property.Writable = true;
property.Readable = true;
properties.AddProperty(property);
});
return properties;
}
}
in my opinion, this method will use a bit more memory
public static class CloneHelper
{
private readonly static JsonSerializerSettings clone_settings = new JsonSerializerSettings() { ContractResolver = new CloneContractResolver() };
public static T Clone<T>(this object source)
{
T entity = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, clone_settings), clone_settings);
return entity;
}
}

Dynamic override of ToString() using Reflection

I generally override the ToString() method to output the property names and the values associated to them. I got a bit tired of writing these by hand so I'm looking for a dynamic solution.
Main:
TestingClass tc = new TestingClass()
{
Prop1 = "blah1",
Prop2 = "blah2"
};
Console.WriteLine(tc.ToString());
Console.ReadLine();
TestingClass:
public class TestingClass
{
public string Prop1 { get; set; }//properties
public string Prop2 { get; set; }
public void Method1(string a) { }//method
public TestingClass() { }//const
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Type type in System.Reflection.Assembly.GetExecutingAssembly().GetTypes())
{
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
sb.Append(property.Name);
sb.Append(": ");
sb.Append(this.GetType().GetProperty(property.Name).Name);
sb.Append(System.Environment.NewLine);
}
}
return sb.ToString();
}
}
This currently outputs:
Prop1: System.String Prop1
Prop2: System.String Prop2
Desired Output:
Prop1: blah1
Prop2: blah2
I'm open for other solutions, it doesn't have to use reflection, it just has to produce the desired output.
This works for me:
public class TestingClass
{
public string Prop1 { get; set; }//properties
public string Prop2 { get; set; }
public void Method1(string a) { }//method
public TestingClass() { }//const
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties())
{
sb.Append(property.Name);
sb.Append(": ");
if (property.GetIndexParameters().Length > 0)
{
sb.Append("Indexed Property cannot be used");
}
else
{
sb.Append(property.GetValue(this, null));
}
sb.Append(System.Environment.NewLine);
}
return sb.ToString();
}
}
To make it available everywhere you can create an Extension.
It's not possible to override methods in an Extension, but still it should simplify your life.
public static class MyExtensions
{
public static string ToStringExtension(this object obj)
{
StringBuilder sb = new StringBuilder();
foreach (System.Reflection.PropertyInfo property in obj.GetType().GetProperties())
{
sb.Append(property.Name);
sb.Append(": ");
if (property.GetIndexParameters().Length > 0)
{
sb.Append("Indexed Property cannot be used");
}
else
{
sb.Append(property.GetValue(obj, null));
}
sb.Append(System.Environment.NewLine);
}
return sb.ToString();
}
}
You can then call ToStringExtension() on every object.
Downside is, it doesn't work perfectly for lists etc., example:
var list = new List<string>();
// (filling list ommitted)
list.ToStringExtension();
// output:
// Capacity: 16
// Count: 11
// Item: Indexed Property cannot be used
Here is an extension which will report the standard types such as string, int and Datetime but will also report string lists (shown below in AccessPoints which the above answer could not handle). Note that the output is aligned such as:
Name : Omegaman
ID : 1
Role : Admin
AccessPoints : Alpha, Beta, Gamma
WeekDays : Mon, Tue
StartDate : 3/18/2014 12:16:07 PM
Below is the extension which takes in any type as long as its a class. It then reflects off of the public and private properties and if they are not null reports them.
public static string ReportAllProperties<T>(this T instance) where T : class
{
if (instance == null)
return string.Empty;
var strListType = typeof(List<string>);
var strArrType = typeof(string[]);
var arrayTypes = new[] { strListType, strArrType };
var handledTypes = new[] { typeof(Int32), typeof(String), typeof(bool), typeof(DateTime), typeof(double), typeof(decimal), strListType, strArrType };
var validProperties = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(prop => handledTypes.Contains(prop.PropertyType))
.Where(prop => prop.GetValue(instance, null) != null)
.ToList();
var format = string.Format("{{0,-{0}}} : {{1}}", validProperties.Max(prp => prp.Name.Length));
return string.Join(
Environment.NewLine,
validProperties.Select(prop => string.Format(format,
prop.Name,
(arrayTypes.Contains(prop.PropertyType) ? string.Join(", ", (IEnumerable<string>)prop.GetValue(instance, null))
: prop.GetValue(instance, null)))));
}
Usage
myInstance.ReportAllProperties()
Note that this is based off my blog article C#: ToString To Report all Properties Even Private Ones Via Reflection which provides a more robust explanation of what is going on.
I would use JSON, Serializer will do all the hard work for you:
public static class ObjectExtensions
{
public static string ToStringEx(this object obj)
{
return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
}
}
This is what I found, that works with most compicated-types (including List):
public static string ToXml(object Obj, System.Type ObjType)
{
try
{
XmlSerializer ser;
XmlSerializerNamespaces SerializeObject = new mlSerializerNamespaces();
ser = new XmlSerializer((ObjType));
MemoryStream memStream;
memStream = new MemoryStream();
XmlTextWriter xmlWriter;
xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
xmlWriter.Namespaces = true;
XmlQualifiedName[] qualiArrayXML = SerializeObject.ToArray();
ser.Serialize(xmlWriter, Obj);
xmlWriter.Close();
memStream.Close();
string xml;
xml = Encoding.UTF8.GetString(memStream.GetBuffer());
xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
return xml;
}
catch (Exception ex)
{ return string.Empty; }
}
usage:
string classAasString = ClassToXml.ToXml(a, typeof(ClassA)); //whare ClassA is an object
I ran into this myself where I am looking for an option to serialize into something readable. If there are no read only properties xml serialization can give a readable string. However if there are read only properties / fields then xml serialization is not an option.
public static string ToString(object serializeable)
{
var type = serializeable.GetType();
try
{
var sw = new StringWriter();
new XmlSerializer(type).Serialize(sw, serializeable);
return sw.ToString();
}
catch
{
return type.FullName;
}
}
So I wrote an extension method that calls a library that has already figured out all the voodoo.
"override string ToString()" vs (my) "ToStringDump"....
Before I show the code, the reason I like the extension method (ToStringDump in this case).. better, is that I don't have to riddle my POCO/DTO objects with ObjectDump references. I believe POCOs and DTOs should be "very very clean", and even isolated in their own assembly. This way, these poco/dto objects are easily shared.
public static class ObjectDumpAdapter
{
public static string ToStringDump(this object obj)
{
string returnValue = ObjectDumper.Dump(obj);
return returnValue;
}
}
My dotnet core csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ObjectDumper.NET" Version="2.5.20033.1" />
</ItemGroup>
</Project>
Nuget link:
https://www.nuget.org/packages/ObjectDumper.NET/
Quote:
ObjectDumper is a utility which aims to serialize C# objects to string
for debugging and logging purposes.
(from https://nugetmusthaves.com/Package/ObjectDumper.NET )
GitHub link:
https://github.com/thomasgalliker/ObjectDumper

Categories