Given the following class:
public class Config {
public string Property1 {get; set;} = "foo";
public string Property2 {get; set;} = "bar";
public string Property3 {get; set;} = "baz";
public string Property4 {get; set;} = "baz1";
}
I want to serialize an instance of this class into two separate JSON strings. Some of the properties should go into the first JSON string:
{
"Property1": "foo",
"Property2": "bar"
}
while the rest of the properties should go into the other JSON string:
{
"Property3": "baz",
"Property4": "baz1"
}
The division of properties is always the same, and properties will go either to one or the other.
(I can deserialize the two JSON strings back into a single object using JObject.Merge.)
Currently, I am writing to a pair of JObject instances, but that is a maintenance nightmare (json["Property1"] = x.Property1; json["Property2"] = x.Property2; etc.).
How can I do this in a way which is easier to maintain?
Making life complicated for no reason but whatever. Here is one way to solve the problem:
Create your own attribute class. Can be either one class per serialization "batch" or one class with the name of the serialization. For example, the name of the attribute class could be JsonSerializationBatchAttribute
Decorate your data members with that attribute like this:
public class Config {
[JsonSerializationBatch("1")]
public string Property1 {get; set;} = "foo";
[JsonSerializationBatch("1")]
public string Property2 {get; set;} = "bar";
[JsonSerializationBatch("2")]
public string Property3 {get; set;} = "baz";
[JsonSerializationBatch("2")]
public string Property4 {get; set;} = "baz1";
}
Then implement conditional serialization as described here:
https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
in the serialization function check if a property has JsonSerializationBatch attribute with proper string and ignore all properties that do not.
I would do this complex thing only if I would have many objects that need this type of serialization. If only one object requires such serialization then I would lean towards splitting class into more than one or using anonymous objects for serialization.
You could try using anonymous objects, like so:
var string1 = JsonConvert.SerializeObject(new {property1: config.Property1, property2: config.Property2});
var string2 = JsonConvert.SerializeObject(new {property3: config.Property3, property4: config.Property4});
PS: I'm assuming you are using newtonsoft.json if not, just replace the serialize method.
Hope to have helped!
I can write a class that inherits from Newtonsoft.Json.Serialization.DefaultContractResolver:
public class ConfigContractResolver : DefaultContractResolver {
private static readonly string[] source1Names = new[] {
nameof(Config.Property1),
nameof(Config.Property2)
};
public bool ForSource1 { get; set; } = true;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
var ret = base.CreateProperties(type, memberSerialization);
var predicate = ForSource1 ?
x => source1Names.Contains(x.PropertyName) :
(Func<JsonProperty, bool>)(x => !source1Names.Contains(x.PropertyName));
ret = ret.Where(predicate).ToList();
return ret;
}
}
and serialize to both strings using an instance of ConfigContractResolver:
Config config = new Config();
// ...
var resolver = new ConfigContractResolver();
var serializationSettings = new JsonSerializerSettings {
ContractResolver = resolver
};
string forSource1 = JsonConvert.SerializeObject(config, serializationSettings);
// It would be nice to reuse the same contract resolver and settings objects
// but that doesn't seem to work -- https://github.com/JamesNK/Newtonsoft.Json/issues/2155
resolver = new ConfigContractResolver() {
ForSource1 = false
};
serializationSettings = new JsonSerializerSettings {
ContractResolver = resolver
};
resolver.ForSource1 = false;
string forSource2 = JsonConvert.SerializeObject(config, serializationSettings);
Source: IContractResolver example
Related
JsonConvert.SerializeObject changes the sorting order of fields in JSON if you call the .GetProperty method on the object being serialized in the child thread.
class Program
{
static void Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
var task = Task.Factory.StartNew(() =>
{
var token = CreateRandomToken();
_ = typeof(TestObject).GetProperty("Version");
var str = JsonConvert.SerializeObject(token);
Console.WriteLine(str);
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
Console.ReadLine();
}
private static TestObject CreateRandomToken()
=> new TestObject { TokenHash = "123456789", Name = "Name", Version = "123" };
}
public class TestObject
{
public string TokenHash { get; set; }
public string Name { get; set; }
public string Version { get; set; }
}
As a result of executing this code, the following will be displayed on the console:
The Version field is at the beginning of the JSON, not at the end
If we remove
_ = typeof(TestObject).GetProperty("Version");
then the sorting of the fields will not change
or if you call code in the main thread, then the sorting will not change either
if I decorate my object with the attributes [JsonProperty (Order = 1)] then the sorting will not be the same as I indicated in the attributes
How can I fix it? fix without using the attr [JsonProperty (Order = 1)]
Updated:
We use a JSON string to generate a digital signature if the order of the fields changes the digital signature will not be valid, so the order of the fields is important for me
Because the default JsonSerializer get properties using System.Type.GetProperties().
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies. (Source Type.GetProperties Method)
In my opinion, you shouldn't care about the order of properties in Json. If the json consumer really need this contract, I think you should review your design.
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array. (Source RFC 7159)
It turns out that JsonConvert.SerializeObject doesn't guarantee the default order of fields. To specify an explicit sorting, you can use the DefaultContractResolver
Thanks Andy for the idea!
Implementation of custom DefaultContractResolver:
public class OrderedContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization).OrderBy(p=>p.PropertyName).ToList();
}
}
Usage example:
var jsonSerializerSettings = new JsonSerializerSettings {ContractResolver = new OrderedContractResolver()};
var str = JsonConvert.SerializeObject(token, jsonSerializerSettings);
My application reads JSON files created using a format like this:
{
"myProperty": {complex JSON here}
}
class MyClass
{
public MyChildClass MyProperty {get; set;}
}
I need to change the way the class works, so that it looks like this instead:
class MyClass
{
public MyNewChildClass MyNewProperty {get; set;}
}
and I need to be able to support files created in the older format, but also support a file if it was created using the new format.
I have the code to convert a MyChildClass object into a MyNewChildClass object, but how can I set up the serialization so that the object can deserialize the old format, changing the property name and type from MyChildClass MyProperty to MyNewChildClass MyNewProperty AND just use the new format when serializing AND deserialize using the new format if that is what the JSON file contains?
If you use both properties, the json will work for both, new and old.
class MyClass
{
[JsonPropert("myProperty")]
public MyChildClass MyProperty {get; set;}
[JsonProperty("myNewProperty")] // -> Remember, case matters.
public MyNewChildClass MyNewProperty {get; set;}
}
When you deserialize the class, add a check to see which is not null and work with that (different methods for each i guess). This should help you keep the breaking change to a minimum.
BTW>. if you have the code that converts the new to the old or vice versa, you can check if the value of old is null, then run that process/method you have to convert new to old and continue on with the object. Remember, it would have to be After the deserialization,
var properties = JsonConvert.DeserializeObject<MyClass>("data");
if (properties.MyNewProperty == null)
{
properties = myMethodToConvertOldToNew(properties);
}
public MyClass myMethodToConvertOldToNew(MyClass)
{
if (properties.New == null)
{
properties.New = ConversionMethod(properties.Old, properties.New);
// dont have to, but,
properties.Old = null;
}
return properties.
}
Why not add a private property to your class that will set your new property:
class MyClass
{
[JsonProperty]
private MyChildClass MyProperty { set => MyNewProperty = YourConversionMethod(value); }
public MyNewChildClass MyNewProperty { get; set; }
}
The JsonProperty attribute will ensure that the private setter is used.
I'm using System.Web.Script.Serialization.JavaScriptSerializer to serialize / deserialize a class that extends Dictionary.
The problem is, my custom properties are not being serialized. Here is my class:
public class Test : Dictionary<string, object> {
public ushort Id { get; set; }
public string Name { get; set; }
}
And my code:
var jss = new JavaScriptSerializer();
var test = new Test {
Id = 123,
Name = "test"
};
var json = jss.Serialize(test);
The result in json is an empty json {}
I do not want to depend on Newtonsoft or JSON.Net or any other library.
ADDITIONAL INFO
I just noticed some, hm, peculiarities, when using both dynamic and object:
JavaScriptSerializer defaults any number value to int.
Also, Newtonsoft defaults any number to long.
That can cause casting exceptions in a class using property indexer (as suggested in the accepted answer), for example:
public class Test : Dictionary<string, dynamic> {
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The Id property getter will try to implicitly convert int to ushort, which will fail.
ADDITIONAL INFO 2
I just found out so many weird behaviors with Newtonsoft:
I added these attributes to solve the 'long to ushort' problem:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The above works! But when the property is a reference type:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
[JsonProperty]
public Test Child { get => this[nameof(Child)]; set => this[nameof(Child)] = value; }
}
It tries to get the property before serializing it, resulting in a 'key not found exception'. I can't see why it tries to get the property only when it's a reference type, seems like a bug to me...
So you have to do something like this:
public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }
Just to summarize the comments:
MSDN recommends using JSON.NET even on JavaScriptSerializer's own docs
Composition will allow you to use JavaScriptSerializer in this case (instead of inheritance)
To work with existing data structure (inheritance), you would have to implement your own version of JavaScriptObjectDeserializer (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
The default serializer only supports attribute to ignore attribute (not include/rename)
To use composition, you would just need to modify your test object like this:
public class Test
{
public ushort Id { get; set; }
public string Name { get; set; }
public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object> {};
}
Then the following code would work fine:
var jss = new JavaScriptSerializer();
var test = new Test
{
Id = 123,
Name = "test",
};
test.Items.Add("A", 1);
var json = jss.Serialize(test);
The output would just be:
{"Id":123,"Name":"test","Items":{"A":1}}
UPDATE: Property Indexer
You could add a default indexer to your class so that the following code would work:
test["A"] = 1;
var result = test["A"];
Here is the code to add for the default indexer:
public object this[string key]
{
get { return this.Items[key]; }
set { this.Items[key] = value; }
}
You could extend this into implementing IDictionary I suppose, but I imagine just working with the composition should be easiest.
Let's say I have a Value that is deserialized from a class.
public class MyValue
{
public string MyPropertyA { get; set; }
public string MyPropertyB { get; set; }
public string DeserializationClass { get; } = typeof(MyValue).Name;
}
I serialize this using JsonConvert class. MyValue class has a property DeserializationClass that should be used as info from which class the string was serialized from. In other words, when I deserialize the string into an object, this property serves as info which class should be used to deserialize the string. However, I am kinda stuck here as I am not sure how to get back the class from the string. Can anybody help me here?
public class Program
{
void Main()
{
var serialized = Serialize();
var obj = Deserialize(serialized);
}
string Serialize()
{
var objValue = new MyValue { MyPropertyA="Something", MyPropertyB="SomethingElse" };
return JsonConvert.SerializeObject<MyClass>(value);
}
object Deserialize(string serialized)
{
//How to deserialize based on 'DeserializationClass' property in serialized string?
return = JsonConvert.Deserialize<???>(serialized);
}
}
EDIT: Modified example to make it more clear what I need as I don't have access to objValue when I need to deserialize the string.
probably you might need to use JsonSerializerSettings.
What you might need to do is
JsonSerializerSettings setting = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
};
and then while serializing use this setting.
var serialized = JsonConvert.SerializeObject(objValue,setting);
this will give you Json like this
{"$type":"WPFDatagrid.MyValue, WPFDatagrid","MyPropertyA":"Something","MyPropertyB":"SomethingElse","DeserializationClass":"MyValue"}
from this you can find the name of the class used it to actually get your type.
Hope this helps !!
There is an overload
If your Type is in form of a Namespace, you can obtain the type from a string representation:
Type objValueType = Type.GetType("Namespace.MyValue, MyAssembly");
object deserialized = JsonConvert.Deserialize(objValueType, serialized);
I want to be able to use the CamelCasePropertyNameContractResolver but override it for specific property names. For this, I use the JsonProperty attribute. This works fine except when the name that I choose is fully uppercase. Any ideas what's wrong or how to get around it?
In the example below, Bar is serialized to "BAR" when I don't use the CamelCasePropertyNameContractResolver, but is serialized to "bar" when I do use the resolver. Foo and CamelCaseProperty are serialized correctly in both scenarios.
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace ConsoleTester
{
class Program
{
static void Main(string[] args)
{
var foo = new FooBar {CamelCaseProperty = "test", Foo = "test", Bar = "test" };
var output = JsonConvert.SerializeObject(foo);
// output "CamelCaseProperty", "fOO", "BAR"
var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
// output "camelCaseProperty", "fOO", "bar"
}
}
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR")]
public string Bar { get; set; }
}
}
The reason you are seeing this is that CamelCasePropertyNamesContractResolver is intentionally designed to override the casing of dictionary keys and explicitly set property names, as can be see from the reference source:
public CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
If you don't want that, you have several options to prevent casing of explicit names without creating your own custom contract resolver type.
Firstly, you could serialize using a DefaultContractResolver with NamingStrategy = new CamelCaseNamingStrategy():
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }
};
var output2 = JsonConvert.SerializeObject(foo, settings);
This leaves CamelCaseNamingStrategy.OverrideSpecifiedNames at its default value of false.
Secondly, if you don't have access to the contract resolver of your framework, you could set JsonPropertyAttribute.NamingStrategyType = typeof(DefaultNamingStrategy) on specific properties, like so:
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR", NamingStrategyType = typeof(DefaultNamingStrategy))]
public string Bar { get; set; }
}
Thirdly, if you want your entire object to ignore the naming strategy of the current contract resolver, you can apply [JsonObject(NamingStrategyType = typeof(TNamingStrategy))] to your object:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class FooBar
{
public string CamelCaseProperty { get; set; }
[JsonProperty("fOO")]
public string Foo { get; set; }
[JsonProperty("BAR")]
public string Bar { get; set; }
}
Notes:
While it is also possible to modify the NamingStrategy of an instance of CamelCasePropertyNamesContractResolver, since the latter shares contract information globally across all instances of each type, this can lead to unexpected side-effects if your application tries to use multiple instances of CamelCasePropertyNamesContractResolver. No such problem exists with DefaultContractResolver, so it is safer to use when any customization of casing logic is required.
When using or subclassing DefaultContractResolver, you may want to cache the contract resolver for best performance, since it does not share contract information globally across all instances of each type.
I don't know why Json.NET's camel case resolver is designed to override specified names, it may be for historical reasons.
Naming strategies were first introduced in Json.NET 9.0.1 so this answer works only for that version and later.
JsonProperty Attribute is not honoured when you use a ContractResolver.
What you can do to solve this issue is override the ContractResolver:
public class MyResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if(member.GetCustomAttribute<JsonPropertyAttribute>() is JsonPropertyAttribute jsonProperty)
{
property.PropertyName = jsonProperty.PropertyName;
}
return property;
}
}
And use your Resolver:
var output2 = JsonConvert.SerializeObject(foo, new JsonSerializerSettings { ContractResolver = new MyResolver() });