Newtonsoft json deserialise missing int values as nulls instead of zero - c#

Is it possible to adjust JsonSerializerSettings that way?
(I guess not) but still have some hope, cause I'm not very experienced with their API.
By default and with all settings I've tried missing int either deserialized to 0 or causing exception.
Example
{
"defaultCaptionLineNum": 6,
"worksheets": {
"0": { "name": "Ws1", "caption_ln": 6 },
"1": { "name": "Ws2", "caption_ln": null },
"2": { "name": "Ws3" },
"3": { "name": "Ws4", "caption_ln": 5 },
}
}
Currently by default caption line (caption_ln) of Ws3 evaluated to 0, I want it to be evaluated to null (as in Ws2).
I'm using
jObject.ToObject<MyClass>(JsonSerializer.Create(settings));
to get object with worksheet(Ws) information from JSON (tried it without serializer as well)
and any variations of Include and Ignore here don't make jObject deserialize missing ints in json differently (with the exception of throwing error right away for missing values)
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
MissingMemberHandling = MissingMemberHandling.Ignore
};
Other serializer settings seem to be irrelevant.
UPD: Listing of MyObject MyClass.
public class MyClass
{
public class WorkSheet
{
[JsonProperty("name")]
string wsName;
[JsonProperty("caption_ln")]
int? captionLineNumber;
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
}
private int _defaultCaptionLineNum;
[JsonProperty("defaultCaptionLineNum")]
public int DefaultCaptionLineNum { get => _defaultCaptionLineNum; }
private Dictionary<int, WorkSheet> _worksheets = new Dictionary<int, WorkSheet>();
[JsonProperty("worksheets")]
public Dictionary<int, WorkSheet> Worksheets { get => _worksheets; set => _worksheets = value; }
public MyClass()
{
Console.WriteLine("New MyClass object created!");
}
}

Your problem is that your type WorkSheet has only one constructor, which is parameterized:
public class WorkSheet
{
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
As explained in the documentation page ConstructorHandling Enumeration, Json.NET will fall back to a single parameterized constructor when there is no parameterless constructor, which is the case here. For the constructor arguments, Json.NET will match the JSON parameters to constructor arguments by name (modulo case), deserializing to the argument type if present or passing a default value if not present. And since there is no "captionln" parameter present in your JSON, captionLineNumber gets initialized to default(int), which is 0. This explains the problem you are seeing.
To fix the problem, you can change captionln to be of type int?:
public class WorkSheet
{
public WorkSheet(string name, int? captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
Working fiddle #1 here.
Alternatively, you could introduce a private serialization-specific constructor and mark it with [JsonConstructor]:
public class WorkSheet
{
[JsonConstructor]
private WorkSheet() { }
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
Sample fiddle #2 here.
Related questions on constructor use in Json.NET:
How does JSON deserialization in C# work (whose answer has a cool diagram).
Json.net `JsonConstructor` constructor parameter names.
JSON.net: how to deserialize without using the default constructor?.

Related

C# Why does System.Text.Json doesnt deserialise string

Given this model class:
using System.Text.Json;
public class QueueMessage
{
public int MerchantId { get; }
public string Message { get; }
}
When I try to deserialize a json string into the type QueueMessage, the fields are set to default. 0 and null.
This is how I've tried to deserialize it:
var jsonString = "{\"MerchantId\":2,\"Message\":\"Message 2\"}";
QueueMessage message = JsonSerializer.Deserialize<QueueMessage>(jsonString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
message.Message is null
message.MerchantId is 0
I'm using .Net 5 by the way.
What have I tried
Well i try to use my ol' buddy Newtonsoft.Json
dynamic message = JsonConvert.DeserializeObject(jsonString);
dynamic mercId = message.MerchantId.Value; //THIS gives the expected value of 2
However,
QueueMessage msg = JsonConvert.DeserializeObject<QueueMessage>(jsonString);
Gives the 0, null result
Now, the question is why does deserializing to a typed object fail?
As an alternative to Ziv's answer, and if you value the benefits of constructed types and/or immutability, recent versions of System.Text.Json now support \[JsonConstructor\], so you can now use constructed DTO types.
In my opinion you should always also specify an explicit [JsonPropertyName] to protect against DTO/JSON breakages caused by renamed properties or changing project-wide camelCase vs PascalCase JSON serialization settings.
It's also my opinion that JSON object members should always use camelCase, not PascalCase as that's the capitalization convention used by JavaScript/TypeScript, which is the usual target for JSON types (unfortunately STJ defaults to PascalCase (*grrrr*))
Example 1: With a simple constructor
...though this isn't a good example as it doesn't show the ctor actually doing anything useful.
using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string? message
)
{
this.MerchantId = merchantId;
this.Message = message;
}
[JsonPropertyName("merchantId")] public int MerchantId { get; }
[JsonPropertyName("message") ] public string? Message { get; }
}
Example 2: With validating constructor
Because the constructor can make assertions and validate parameters it means the ctor can prevent QueueMessage.Message from ever being null, so the String? property can be changed to String which makes consuming the DTO nicer - and it can also validate MerchantId too:
using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string message
)
{
this.MerchantId = merchantId > 0 ? merchantId : throw new ArgumentOutOfRangeException( paramName: nameof(merchantId), actualValue: merchantId, message: "Value must be positive and non-zero." );
this.Message = message ?? throw new ArgumentNullException(nameof(message));
}
/// <summary>Always >= 1.</summary>
[JsonPropertyName("merchantId")] public int MerchantId { get; }
/// <summary>Never null.</summary>
[JsonPropertyName("message") ] public string Message { get; }
}
Example 3: With record class
To keep things simpler, you could also just use a record class.
Though this loses the validation logic, so Message is now String? again.
Use the syntax [property: ] to apply JsonPropertyName and other attributes to record class properties via their ctor parameter names.
public record class QueueMessage(
[property: JsonPropertyName("merchantId")] int MerchantId,
[property: JsonPropertyName("message") ] string? Message
);
You need to add setters to your properties, otherwise the deserializer can't assign values to those properties
public class QueueMessage
{
public int MerchantId { get; set; }
public string Message { get; set; }
}

Is there a way to define classes inline upon their usage?

I'm writing a service to retrieve data from an API over HTTP from another team in my company. The JSON response body from their API looks a bit like this:
"SomeObject": {
"SomeInnerObject": {
"SomeProperty": {
"Id": "123",
"Type": "abc",
"Name": "some value"
}
}
}
I'm writing a C# class to store the data in memory in order to do some comparisons. The nesting of the JSON object causes the class to look annoyingly repetitive:
public class MyClass
{
public SomeObjectModel SomeObject { get; set; }
public class SomeObjectModel
{
public SomeInnerObjectModel InnerObject { get; set; }
public class SomeInnerObjectModel
{
// etc...
}
}
}
I know for sure that the inner classes, like "SomeObjectModel", are only going to be read from and not instantiated elsewhere, so is there a way to combine the class definition and property definition lines into something more like this?
public class MyClass
{
public SomeObject { get; set; } :
{
public SomeInnerObject { get; set; } :
{
// etc...
}
}
}
EDIT:
The JSON will have arrays in it, so take that into account if you are proposing an alternative using generics, etc.
If you're using this just to deserialize the JSON, you don't even need to define a class.. You can use a nested anonymous type.
First, create and populate (with dummy values) an anonymous type that has the properties you need to be able to read. Then pass it as the second parameter to DeserializeAnonymousType<T>(string,T).
The result is a new instance of the same anonymous type that you created, but now it's populated with values from the JSON.
var json = #"{'SomeObject': {'SomeInnerObject': {'SomeProperty': {'Id': '123','Type': 'abc','Name': 'some value'}}}}";
var template = new
{
SomeObject = new
{
SomeInnerObject = new
{
SomeProperty = new
{
Id = default(int),
Type = default(string),
Name = default(string)
}
}
}
};
var result = JsonConvert.DeserializeAnonymousType(json, template);
var id = result.SomeObject.SomeInnerObject.SomeProperty.Id;
var type = result.SomeObject.SomeInnerObject.SomeProperty.Type;
var name = result.SomeObject.SomeInnerObject.SomeProperty.Name;
Console.WriteLine("{0} {1} {2}", id, type, name);
Output:
123 abc some value
See my working example on DotNetFiddle.
Edit: If your JSON contains an array, you can use new[] {} to create an array based on type inference and then put the anonymous types inside, like this:
var json = #"{ 'SomeObjects': [ { 'Id': '123', 'Name': 'some value' }, { 'Id': '456', 'Name': 'another value' } ]}";
var template = new
{
SomeObjects = new [] { new { Id=default(int), Name=default(string)} }
};
The short answer is no, C# does not support any version of that syntactic sugar. The closest thing in C# is probably either anonymous types or value tuples.
Other languages have similar features:
C++ supports "inline classes" in declarations.
Java supports anonymous classes that are more sophisticated than C# anonymous record types.
Scala supports case classes which declare their members in the header of the declaration.
And so on. I think the first is the closest thing to what you are looking for.
Scala-style class declarations have been proposed for C# many times in the last decade, and may finally make it into C# 8; see https://blog.cdemi.io/whats-coming-in-c-8-0-records/
You can use the dynamic type. Here is a minimal example:
using Newtonsoft.Json;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
dynamic x = JsonConvert.DeserializeObject("[{key: '1001', value: 'test'}, {key: '1002', value: 'test2'}, ]");
Console.WriteLine(x[0].key);
Console.WriteLine(x[0].value);
Console.WriteLine(x[1].key);
Console.WriteLine(x[1].value);
Console.ReadLine();
}
}
}
You can create classes by using the paste special, paste JSON as classes.
https://channel9.msdn.com/Series/Windows-Store-Developer-Solutions/Quckly-Generate-C-Classes-from-JSON-Responses#time=01m56s

ASP.NET Web API 2 and partial updates

We are using ASP.NET Web API 2 and want to expose ability to partially edit some object in the following fashion:
HTTP PATCH /customers/1
{
"firstName": "John",
"lastName": null
}
... to set firstName to "John" and lastName to null.
HTTP PATCH /customers/1
{
"firstName": "John"
}
... in order just to update firstName to "John" and do not touch lastName at all. Suppose we have a lot of properties that we want to update with such semantic.
This is quite convenient behavior that is exercised by OData for instance.
The problem is that default JSON serializer will just come up with null in both cases, so it's impossible to distinguish.
I'm looking for some way to annotate model with some kind of wrappers (with value and flag set/unset inside) that would allow to see this difference. Any existing solutions for this?
I know that answers which are already given cover all aspects already, but just want to share concise summary of what we ended up doing and what seems to work for us pretty well.
Created a generic data contract
[DataContract]
public class RQFieldPatch<T>
{
[DataMember(Name = "value")]
public T Value { get; set; }
}
Created ad-hoc data cotnracts for patch requests
Sample is below.
[DataContract]
public class PatchSomethingRequest
{
[DataMember(Name = "prop1")]
public RQFieldPatch<EnumTypeHere> Prop1 { get; set; }
[DataMember(Name = "prop2")]
public RQFieldPatch<ComplexTypeContractHere> Prop2 { get; set; }
[DataMember(Name = "prop3")]
public RQFieldPatch<string> Prop3 { get; set; }
[DataMember(Name = "prop4")]
public RQFieldPatch<int> Prop4 { get; set; }
[DataMember(Name = "prop5")]
public RQFieldPatch<int?> Prop5 { get; set; }
}
Business Logic
Simple.
if (request.Prop1 != null)
{
// update code for Prop1, the value is stored in request.Prop1.Value
}
Json format
Simple. Not that extensive as "JSON Patch" standard, but covers all our needs.
{
"prop1": null, // will be skipped
// "prop2": null // skipped props also skipped as they will get default (null) value
"prop3": { "value": "test" } // value update requested
}
Properties
Simple contracts, simple logic
No serialization customization
Support for null values assignment
Covers any types: value, reference, complex custom types, whatever
At first I misunderstood the problem. As I was working with Xml I thought it was quite easy. Just add an attribute to the property and leave the property empty. But as I found out, Json doesn't work like that. Since I was looking for a solution that works for both xml and json, you'll find xml references in this answer. Another thing, I wrote this with a C# client in mind.
The first step is to create two classes for serialization.
public class ChangeType
{
[JsonProperty("#text")]
[XmlText]
public string Text { get; set; }
}
public class GenericChangeType<T> : ChangeType
{
}
I've chosen for a generic and a non-generic class because it is hard to cast to a generic type while this is not important. Also, for xml implementation it is necessary that XmlText is string.
XmlText is the actual value of the property. The advantage is that you can add attributes to this object and the fact that this is an object, not just string. In Xml it looks like: <Firstname>John</Firstname>
For Json this doesn't work. Json doesn't know attributes. So for Json this is just a class with properties. To implement the idea of the xml value (I will get to that later), I've renamed the property to #text. This is just a convention.
As XmlText is string (and we want to serialize to string), this is fine to store the value disregard the type. But in case of serialization, I want to know the actual type.
The drawback is that the viewmodel needs to reference these types, the advantage is that the properties are strongly typed for serialization:
public class CustomerViewModel
{
public GenericChangeType<int> Id { get; set; }
public ChangeType Firstname { get; set; }
public ChangeType Lastname { get; set; }
public ChangeType Reference { get; set; }
}
Suppose I set the values:
var customerViewModel = new CustomerViewModel
{
// Where int needs to be saved as string.
Id = new GenericeChangeType<int> { Text = "12" },
Firstname = new ChangeType { Text = "John" },
Lastname = new ChangeType { },
Reference = null // May also be omitted.
}
In xml this will look like:
<CustomerViewModel>
<Id>12</Id>
<Firstname>John</Firstname>
<Lastname />
</CustomerViewModel>
Which is enough for the server to detect the changes. But with json it will generate the following:
{
"id": { "#text": "12" },
"firstname": { "#text": "John" },
"lastname": { "#text": null }
}
It can work, because in my implementation the receiving viewmodel has the same definition. But since you are talking about serialization only and in case you use another implementation you would want:
{
"id": 12,
"firstname": "John",
"lastname": null
}
That is where we need to add a custom json converter to produce this result. The relevant code is in WriteJson, assuming you would add this converter to the serializer settings only. But for the sake of completeness I've added the readJson code as well.
public class ChangeTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// This is important, we can use this converter for ChangeType only
return typeof(ChangeType).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = JToken.Load(reader);
// Types match, it can be deserialized without problems.
if (value.Type == JTokenType.Object)
return JsonConvert.DeserializeObject(value.ToString(), objectType);
// Convert to ChangeType and set the value, if not null:
var t = (ChangeType)Activator.CreateInstance(objectType);
if (value.Type != JTokenType.Null)
t.Text = value.ToString();
return t;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var d = value.GetType();
if (typeof(ChangeType).IsAssignableFrom(d))
{
var changeObject = (ChangeType)value;
// e.g. GenericChangeType<int>
if (value.GetType().IsGenericType)
{
try
{
// type - int
var type = value.GetType().GetGenericArguments()[0];
var c = Convert.ChangeType(changeObject.Text, type);
// write the int value
writer.WriteValue(c);
}
catch
{
// Ignore the exception, just write null.
writer.WriteNull();
}
}
else
{
// ChangeType object. Write the inner string (like xmlText value)
writer.WriteValue(changeObject.Text);
}
// Done writing.
return;
}
// Another object that is derived from ChangeType.
// Do not add the current converter here because this will result in a loop.
var s = new JsonSerializer
{
NullValueHandling = serializer.NullValueHandling,
DefaultValueHandling = serializer.DefaultValueHandling,
ContractResolver = serializer.ContractResolver
};
JToken.FromObject(value, s).WriteTo(writer);
}
}
At first I tried to add the converter to the class: [JsonConverter(ChangeTypeConverter)]. But the problem is that the converter will be used at all times, which creates a reference loop (as also mentioned in the comment in the code above). Also you may want to use this converter for serialization only. That is why I've added it to the serializer only:
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new ChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var s = JsonConvert.SerializeObject(customerViewModel, serializerSettings);
This will generate the json I was looking for and should be enough to let the server detect the changes.
-- update --
As this answer focusses on serialization, the most important thing is that lastname is part of the serialization string. It then depends on the receiving party how to deserialize the string into an object again.
Serialization and deserialization use different settings. In order to deserialize again you can use:
var deserializerSettings = new JsonSerializerSettings
{
//NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new Converters.NoChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var obj = JsonConvert.DeserializeObject<CustomerViewModel>(s, deserializerSettings);
If you use the same classes for deserialization then Request.Lastname should be of ChangeType, with Text = null.
I'm not sure why removing the NullValueHandling from the deserialization settings causes problems in your case. But you can overcome this by writing an empty object as value instead of null. In the converter the current ReadJson can already handle this. But in WriteJson there has to be a modification. Instead of writer.WriteValue(changeObject.Text); you need something like:
if (changeObject.Text == null)
JToken.FromObject(new ChangeType(), s).WriteTo(writer);
else
writer.WriteValue(changeObject.Text);
This would result in:
{
"id": 12,
"firstname": "John",
"lastname": {}
}
Here's my quick and inexpensive solution...
public static ObjectType Patch<ObjectType>(ObjectType source, JObject document)
where ObjectType : class
{
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
try
{
String currentEntry = JsonConvert.SerializeObject(source, settings);
JObject currentObj = JObject.Parse(currentEntry);
foreach (KeyValuePair<String, JToken> property in document)
{
currentObj[property.Key] = property.Value;
}
String updatedObj = currentObj.ToString();
return JsonConvert.DeserializeObject<ObjectType>(updatedObj);
}
catch (Exception ex)
{
throw ex;
}
}
When fetching the request body from your PATCH based method, make sure to take the argument as a type such as JObject. JObject during iteration returns a KeyValuePair struct which inherently simplifies the modification process. This allows you to get your request body content without receiving a deserialized result of your desired type.
This is beneficial due to the fact that you don't need any additional verification for nullified properties. If you want your values to be nullified that also works because the Patch<ObjectType>() method only loops through properties given in the partial JSON document.
With the Patch<ObjectType>() method, you only need to pass your source or target instance, and the partial JSON document that will update your object. This method will apply camelCase based contract resolver to prevent incompatible and inaccurate property names from being made. This method will then serialize your passed instance of a certain type and turned into a JObject.
The method then replaces all properties from the new JSON document to the current and serialized document without any unnecessary if statements.
The method stringifies the current document which now is modified, and deserializes the modified JSON document to your desired generic type.
If an exception occur, the method will simply throw it. Yes, it is rather unspecific but you are the programmer, you need to know what to expect...
This can all be done on a single and simple syntax with the following:
Entity entity = AtomicModifier.Patch<Entity>(entity, partialDocument);
This is what the operation would normally look like:
// Partial JSON document (originates from controller).
JObject newData = new { role = 9001 };
// Current entity from EF persistence medium.
User user = await context.Users.FindAsync(id);
// Output:
//
// Username : engineer-186f
// Role : 1
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Partially updated entity.
user = AtomicModifier.Patch<User>(user, newData);
// Output:
//
// Username : engineer-186f
// Role : 9001
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Setting the new values to the context.
context.Entry(user).State = EntityState.Modified;
This method will work well if you can correctly map your two documents with the camelCase contract resolver.
Enjoy...
Update
I updated the Patch<T>() method with the following code...
public static T PatchObject<T>(T source, JObject document) where T : class
{
Type type = typeof(T);
IDictionary<String, Object> dict =
type
.GetProperties()
.ToDictionary(e => e.Name, e => e.GetValue(source));
string json = document.ToString();
var patchedObject = JsonConvert.DeserializeObject<T>(json);
foreach (KeyValuePair<String, Object> pair in dict)
{
foreach (KeyValuePair<String, JToken> node in document)
{
string propertyName = char.ToUpper(node.Key[0]) +
node.Key.Substring(1);
if (propertyName == pair.Key)
{
PropertyInfo property = type.GetProperty(propertyName);
property.SetValue(source, property.GetValue(patchedObject));
break;
}
}
}
return source;
}
I know I'm a little bit late on this answer, but I think I have a solution that doesn't require having to change serialization and also doesn't include reflection (This article refers you to a JsonPatch library that someone wrote that uses reflection).
Basically create a generic class representing a property that could be patched
public class PatchProperty<T> where T : class
{
public bool Include { get; set; }
public T Value { get; set; }
}
And then create models representing the objects that you want to patch where each of the properties is a PatchProperty
public class CustomerPatchModel
{
public PatchProperty<string> FirstName { get; set; }
public PatchProperty<string> LastName { get; set; }
public PatchProperty<int> IntProperty { get; set; }
}
Then your WebApi method would look like
public void PatchCustomer(CustomerPatchModel customerPatchModel)
{
if (customerPatchModel.FirstName?.Include == true)
{
// update first name
string firstName = customerPatchModel.FirstName.Value;
}
if (customerPatchModel.LastName?.Include == true)
{
// update last name
string lastName = customerPatchModel.LastName.Value;
}
if (customerPatchModel.IntProperty?.Include == true)
{
// update int property
int intProperty = customerPatchModel.IntProperty.Value;
}
}
And you could send a request with some Json that looks like
{
"LastName": { "Include": true, "Value": null },
"OtherProperty": { "Include": true, "Value": 7 }
}
Then we would know to ignore FirstName but still set the other properties to null and 7 respectively.
Note that I haven't tested this and I'm not 100% sure it would work. It would basically rely on .NET's ability to serialize the generic PatchProperty. But since the properties on the model specify the type of the generic T, I would think it would be able to. Also since we have "where T : class" on the PatchProperty declaration, the Value should be nullable. I'd be interested to know if this actually works though. Worst case you could implement a StringPatchProperty, IntPatchProperty, etc. for all your property types.

Using Newtonsoft.Json with nested custom classes

I need to serialize some custom objects in order to store information. However, I am struggling to deserialize those objects from the serialized JSON string back into their original object forms.
The serialized string seems fine:
[
{
"MyStringArray": [
"stringInput1",
"stringInput2"
],
"MyCharArray": [
"a",
"b",
"c",
"."
],
"MyString": "dummy",
"MyClass3Object": [
{
"MyString": "ListInput1"
},
{
"MyString": "ListInput2"
}
]
}
]
However, when I reconstruct the original MyClass1 object, the list has one entry as it should but it is filled with nulls instead of the corresnponding data. Any ideas as to what may be happening? Thanks in advance for the brainstorming :)
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.IO;
using System.Text.RegularExpressions;
namespace JsonTesting
{
class Program
{
static void Main(string[] args)
{
MyClass1 c1 = new MyClass1();
c1.AddInfo();
string toJsonString = JsonConvert.SerializeObject(c1, Formatting.Indented,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Include });
File.WriteAllText(#"C:\temp\dumpJsonText.txt",toJsonString);
MyClass1 fromJson = JsonConvert.DeserializeObject<MyClass1>(toJsonString);
Console.ReadLine();
}
}
public class MyClass1 : List<MyClass2> {
public MyClass1() { }
public void AddInfo() {
this.Add(new MyClass2(new string[] { "stringInput1", "stringInput2" },
new char[] { 'a', 'b', 'c', '.' },
"dummy",
new List<MyClass3>() { new MyClass3("ListInput1", new Regex(#"[A-Z]")), new MyClass3("ListInput2", new Regex(#"[0-9]")) }
));
}
}
public class MyClass2
{
private string[] _myStringArray = null;
private char[] _myCharArray = null;
private string _myString = null;
private List<MyClass3> _myClass3Object = null;
public MyClass2() { }
public MyClass2(string[] myStringArray, char[] myCharArray, string myString, List<MyClass3> myClass3Object)
{
_myStringArray = myStringArray;
_myCharArray = myCharArray;
_myString = myString;
_myClass3Object = myClass3Object;
}
public string[] MyStringArray { get { return _myStringArray; } }
public char[] MyCharArray { get { return _myCharArray; } }
public string MyString { get { return _myString; } }
public List<MyClass3> MyClass3Object { get { return _myClass3Object; } }
}
public class MyClass3 {
private Regex _myRegex;
private string _myString = null;
public MyClass3() { }
public MyClass3(string myString, Regex myRegex) {
_myString = myString;
_myRegex = myRegex;
}
public string MyString{ get {return _myString;} }
}
}
Your classes MyClass2 and MyClass3 are read-only. In order for Json.NET to deserialize a read-only type, you must either provide a custom JsonConverter that manually deserializes and constructs an instance of the type, or provide a parameterized constructor whose argument names match the property names modulo case. You have already created the necessary constructors and so are halfway done.
However your types have parameterless constructors as well. So, which constructor does Json.NET call? For a non-enumerable type that is serialized to a JSON object, the following rules apply:
If [JsonConstructor] is set on a constructor, use that constructor.
Next, in full trust only, when MemberSerialization.Fields is applied, or [Serializable] is applied and DefaultContractResolver.IgnoreSerializableAttribute == false, the special method FormatterServices.GetUninitializedObject() is used to allocate the object. None of the type's constructors are called.
(This is an unusual case that does not arise often.)
Next, if there is a public parameterless constructor, use it.
Next, if a private parameterless constructor exists and the setting ConstructorHandling.AllowNonPublicDefaultConstructor
is enabled, the private parameterless constructor is used.
Next, if there is a single public parameterized constructor, use that constructor.
Failing all of the above, Json.NET cannot construct instances of the type. An exception will get thrown during deserialization unless a custom converter is available.
Thus the parameterless constructors take precedence over the parameterized constructors. To force the parameterized constructors to be used, mark them with [JsonConstructor] as mentioned above:
public class MyClass3
{
private Regex _myRegex;
private string _myString = null;
public MyClass3() { }
[JsonConstructor]
// The argument names must match the property names modulo case for Json.NET to deserialize the properties successfully.
public MyClass3(string myString, Regex myRegex)
{
_myString = myString;
_myRegex = myRegex;
}
public string MyString { get { return _myString; } }
public Regex MyRegex { get { return _myRegex; } }
}
Alternatively, you could eliminate the parameterless constructor as it apparently did not exist in the first version of your question. Then make the same change to MyClass2. Now your types will deserialize successfully.
Note that Json.NET has a built-in converter for serializing a Regex.
Sample fiddle.

Determining object "type" during json deserialization

While trying to de-serialize a complex JSON object (JIRA issue) into an object containing a dictionary of type string-Field I've hit a bit of a bump.
While I can de-serialize various pre-determined object types (standard), I'm having a bit of a harder time with the custom fields, which could be of various types (they all begin with customfield_ followed by a set of numbers).
The custom fields can be floats, strings, booleans, objects and arrays of objects. The latter of these is causing me issues since I can't seem to determine what the object is before I de-serialize it.
I've searched for a way to perhaps "peek" at the data in the object before de-serializing as one of the fields contains information specific to it's type. This is all so I can determine the type of the object and tell Json.Net what to de-serialize it as.
I've considered parsing the JSON string before serialization to get the information, or maybe just when hitting this particular case, but maybe there is a better way?
Thanks in advance for any advice on this.
You can deserialize to an object with Json.Net. Here's a quick and dirty example:
using System;
using Newtonsoft.Json;
namespace Sandbox
{
class Program
{
private static void Main(string[] args)
{
var nestDto = new Dto
{
customfield_1 = 20,
customfield_2 = "Test2"
};
var dto = new Dto
{
customfield_1 = 10,
customfield_3 = new[] { nestDto },
customfield_2 = "Test"
};
var jsonString = JsonConvert.SerializeObject(dto);
Console.WriteLine(jsonString);
var fromJsonString = JsonConvert.DeserializeObject<Dto>(jsonString);
Console.WriteLine(fromJsonString.customfield_3[0].customfield_2); //Outputs Test2
Console.ReadKey();
}
}
class Dto
{
public int customfield_1 { get; set; }
public string customfield_2 { get; set; }
public Dto[] customfield_3 { get; set; }
}
}
Instead of peaking, you can deserialize as the same type as JSON.net uses for ExtensionData explicitly. For example:
if (reader.TokenType == JsonToken.StartArray)
{
var values = serializer.Deserialize<List<Dictionary<string, JToken>>>(reader);
objectContainer = ClassifyAndReturn(values);
}
private ObjectType ClassifyAndReturn(List<Dictionary<string, JToken>> values)
{
if (values.First().ContainsKey("self"))
{
string self = values.First()["self"].Value<string>();
if (self.Contains("customFieldOption"))
//... Then go into a series of if else cases to determine the object.
The representation of the objects are given as a Dictionary of string to JToken, which can then easily be checked and assigned manually or in some cases automatically deserialized (in the case one of the fields is another object).
Here is what an object constructor could look like:
internal myobject(Dictionary<string, JToken> source)
{
Self = source["self"].Value<string>();
Id = source["id"].Value<string>();
Value = source["value"].Value<string>();
}

Categories