Newtonsoft.Json.JsonConvert.DeserializeObject into Newtonsoft.Json.Linq.JObject issues - c#

I am working on a C# project.
[
{"FirstName":"XYZ","LastName":"SSS"},
{"FirstName":"ABC","LastName":"NNN"}
]
Each row represents an object of class ChildDTO.
I read the above data from the file and am trying to deserialize into a ParentCollection object like below:
string file = System.IO.File.ReadAllText("Children_CA.txt");
ParentCollection pCollection = Newtonsoft.Json.JsonConvert.DeserializeObject<ParentCollection>(file);
My DTO classes are like below:
[Serializable]
public class ParentCollection : CollectionBase
{
public void Add(ChildDTO dto)
{
//List is from Systems.Collections.CollectionBase class
List.Add(dto);
}
}
[Serializable]
public class ChildDTO
{
// properties like FirstName and LastName goes here
}
I cannot change my DTO classes since they are old and already in production from the past 20 years and many applications are using them.
When I see Quick Watch on pCollection, I notice the collection is having objects of type Newtonsoft.Json.Linq.JObject. I am hoping to have objects of type ChildDTO
Please let me know what mistake I am doing.

The problem is that CollectionBase, and by extension your ParentCollection class, are not generic, so Json.Net doesn't know that the items in the collection are supposed to be ChildDTO objects. Since it doesn't know what type the items are, it just uses JObject instead. You can fix the problem using a custom JsonConverter class like this:
class ParentCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ParentCollection);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
ParentCollection pc = new ParentCollection();
JArray array = JArray.Load(reader);
foreach (var item in array)
{
pc.Add(item.ToObject<ChildDTO>());
}
return pc;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, pass it to DeserializeObject<T> like this:
ParentCollection pCollection =
JsonConvert.DeserializeObject<ParentCollection>(json, new ParentCollectionConverter());
Working demo here: https://dotnetfiddle.net/3GM22c

Good morning. If you need the ChildDto's list, please convert it to List. Please refer to the code and image below. Many thanks.
string file = System.IO.File.ReadAllText("Children_CA.txt");
var results1 = JsonConvert.DeserializeObject<List<ChildDTO>>(file);

Related

JSON.Net - How to deserialize a nested field of a custom type

I've been having trouble finding a question about my particular case:
I need to deserialize the following JSON:
{
"name": "My Farm",
"barns": [
{
"name"": "Barn A"",
"animalTypes": [
"Cow",
"Goat"
]
}
]
}
to the following code model:
public class Farm
{
public string name;
public Barn[] barns;
}
public class Barn
{
public string name;
public AnimalType[] animalTypes;
}
public class AnimalType
{
public int typeID;
}
The problem: I need to deserialize a JSON string (which described an animal species name) into an 'AnimalType' object which contains a "type ID" int. This requirement is not something I can change.
To get the animal's integer type ID, I have access to an externally supplied "AnimalTypeResolver" class, which looks like this:
public class AnimalTypeResolver
{
public int GetAnimalType(string animalTypeName)
{
// queries a map to return the right ID.
}
}
I can query that resolver to get the int value I need to store for each animal type.
So, I tried to write a custom JSONConverter for AnimalType:
public class AnimalTypeConverter : JsonConverter<AnimalType>
{
public AnimalTypeResolver animalTypeResolver;
public AnimalTypeConverter(AnimalTypeResolver animalTypeResolver)
{
this.animalTypeResolver = animalTypeResolver;
}
public override bool CanWrite => false;
public override AnimalType ReadJson(ref JsonReader reader, Type objectType, AnimalType existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
string animalTypeName = (string)reader.Value;
return new AnimalType
{
typeID = animalTypeResolver.GetAnimalType(animalTypeName)
};
}
return null;
}
public override void WriteJson(JsonWriter writer, AnimalType value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And my deserialization code looks like this:
string farmJSON =
#"{
""name"": ""My Farm"",
""barns"": [
{
""name"": ""Barn A"",
""animalTypes"": [
""Cow"",
""Goat""
]
}
]
}";
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new AnimalTypeConverter(animalTypeResolver)); // this animalTypeResolver is supplied from elsewhere in code.
Farm farm = JsonConvert.DeserializeObject<Farm>(farmJSON);
But I get a runtime error:
ArgumentException: Could not cast or convert from System.String to AnimalType.
From reading similar questions, I believe my problem is that I'm trying to deserialize a nested field of a custom type (AnimalType[]). Other answers have explained that JToken.FromObject() creates a new JsonSerializer for each level of deserialization, which has no concept of the JsonConverters I added to a higher-level serializer.
However, For one reason or another, the other questions on this site have answers which aren't exactly applicable to my case.
How I can use my custom JsonConverter, to handle a case in which the data is deeply nested?
If anyone can offer advice about how to make this work, thank you!
JsonConvert.DeserializeObject<Farm>(farmJSON) does not use your JsonSerializer instance. I mean, how could it possibly access the serializer variable that you created and assigned in your code?
You have two choices: Either use the Deserialize method of the serializer instance you did just setup to deserialize your json data and not use JsonConvert.DeserializeObject .
Or instead of setting up a serializer, define some JsonSerializerSettings with your custom JsonConverter and pass those settings to the JsonConvert.DeserializeObject method. Alternatively you could instead also define default serialization settings for JsonConvert.DeserializeObject as demonstrated here in the Newtonsoft.Json documentation: https://www.newtonsoft.com/json/help/html/DefaultSettings.htm

How to represent and serialize a data structure which requires duplicate keys?

I'm working with a third-party API where you send an order along with an array of options you want to add to the order. Each Option has an OptionId.
You'd assume the required JSON would look something like this:
{
//More properties cut for brevity
"options":[
{
"optionId":"ID 1"
},
{
"optionId":"ID 2"
}
]
}
Instead, the required JSON actually looks like this:
{
//More properties cut for brevity
"options":[
{
"optionId":"ID 1",
"optionId":"ID 2"
}
]
}
Is there any way I can represent that data structure in C#? And if yes, is there any way I can tell Json.NET to serialize it the way the API requires?
The desired JSON is technically valid, though not recommended because having duplicate keys in an object makes it much more difficult to work with. I understand this is a third party API, and you don't have any other choice, but I want to make clear to future readers that this is a bad design.
To represent the option IDs in C#, you can use a simple List<string>. You will need to make a custom JsonConverter to write out the desired structure to the JSON. We'll call it OptionListConverter. So declare the option list as shown below:
class Order
{
[JsonProperty("options")]
[JsonConverter(typeof(OptionListConverter))]
public List<string> OptionIds { get; set; }
}
Then define the OptionListConverter like this:
class OptionListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<string>);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var list = (List<string>)value;
writer.WriteStartArray();
writer.WriteStartObject();
foreach (var id in list)
{
writer.WritePropertyName("optionId");
writer.WriteValue(id);
}
writer.WriteEndObject();
writer.WriteEndArray();
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can then create the desired JSON like this:
var order = new Order
{
OptionIds = new List<string> { "ID 1", "ID 2" }
};
var json = JsonConvert.SerializeObject(order, Formatting.Indented);
Working demo: https://dotnetfiddle.net/rEYl8p

Deserializing name / value pairs to objects

I have a collection of name / value pairs where they are defined with the words name and value just like a Key/Value object, i.e.
[{"Name":"ActivityId","DataType":1,"Value":"a7868f8c-07ac-488d-a414-714527c2e76f"},
{"Name":"Address1","DataType":2,"Value":"123 Main St"}]
If I had an object like:
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
How can I deserialize this to the class above?
Should I consider a custom converter? Does Json.NET have something built-in? Is there a way to decorate the properties with an attribute that I'm missing? Would it be easier to customize the serialization?
I'm trying to avoid pulling the data for each property from a Dictionary, which would be the easy route, but would require me to do this with each custom implementation. I would prefer to do this in a base class in a single method using Json.NET (or something in the .NET framework).
I've searched quite a bit, and most examples are real name/value pairs, not prefixed with name and value, i.e.
[{"ActivityId":"a7868f8c-07ac-488d-a414-714527c2e76f"}]
Any ideas?
This can be done in a straightforward manner with a custom JsonConverter like the one below. The converter works by first transforming the array of name-value pairs into a JObject with properties mirroring the pairs, then populating the target object from the JObject using the serializer's built-in Populate method.
class NameValueConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the array of name-value pairs and transform into a JObject.
// We are assuming all the names will be distinct.
JObject obj = new JObject(
JArray.Load(reader)
.Children<JObject>()
.Select(jo => new JProperty((string)jo["Name"], jo["Value"]))
);
// Instantiate the target object and populate it from the JObject.
object result = Activator.CreateInstance(objectType);
serializer.Populate(obj.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// WriteJson is not called when CanWrite returns false
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
// We only want this converter to handle classes that are expressly
// marked with a [JsonConverter] attribute, so return false here.
// (CanConvert is not called when [JsonConverter] attribute is used.)
return false;
}
}
To use the converter, just add a [JsonConverter] attribute to the target class:
[JsonConverter(typeof(NameValueConverter))]
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
Then, you can deserialize as you normally would:
Request req = JsonConvert.DeserializeObject<Request>(json);
Fiddle: https://dotnetfiddle.net/tAp1Py

Declared properties won't be serialized if its declaring type is a dynamic object

Let's say I've the following dynamic object:
public class SomeDynamicObject : DynamicObject
{
public string Text { get; set; }
}
If I serialize it using JsonConvert.SerializeObject(new SomeDynamicObject { Text = "hello world" }) it'll return {} instead of { "Text": "hello world" }.
I suspect the issue is that JSON.NET thinks it's a full dynamic object while my case is a dynamic object with declared members.
Is there any serialization settings or built-in converter that could be configured so JSON.NET can serialize both kinds of members?
To avoid confusion
Actual use case: I don't know which will be the types being serialized but I need to cover the whole use case of serializing declared properties of a dynamic object.
That is, I can't use attributes. That's why I'm asking if there's some converter or a serialization setting that can generalize this use case.
Update for non-attribute converter
Since you can't decorate, you lose a lot of power. Once the JsonWriter has converted to a JObject, the dynamic properties appear to be lost.
However, you can always use a little reflection in a custom converter's WriteJson method to serialize non-dynamic types.
public class SomeDynamicObject : DynamicObject
{
public string Text { get; set; }
public DynamicObject DynamicProperty { get; set; }
}
public class CustomDynamicConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
var target = Activator.CreateInstance(objectType);
//Create a new reader for this jObject, and set all properties to match the original reader.
JsonReader jObjectReader = jObject.CreateReader();
jObjectReader.Culture = reader.Culture;
jObjectReader.DateParseHandling = reader.DateParseHandling;
jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jObjectReader.FloatParseHandling = reader.FloatParseHandling;
// Populate the object properties
serializer.Populate(jObjectReader, target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var properties = value.GetType().GetProperties().Where(x => x.PropertyType != typeof(DynamicObject)).ToList();
JObject o = (JObject)JToken.FromObject(value);
properties.ForEach(x =>
{
o.AddFirst(new JProperty(x.Name, x.GetValue(value)));
});
o.WriteTo(writer);
}
}
If you explicitly decorate your properties with [JsonProperty], the serializer will pick them up, even if the containing type is dynamic.
public class SomeDynamicObject : DynamicObject
{
[JsonProperty]
public string Text { get; set; }
}
when serialized correctly outputs:
{"Text":"hello world"}

Proper way of using Newtonsoft Json ItemConverterType

I have some bad data coming back from a web service which I cannot change. The service returns a JSON list of customers. Inside this list, each customer also has a list of jobs. But the JSON coming back is a string for the jobs.
So: Jobs: "[]" Instead of Jobs: []
So I defined the class as
[JsonProperty(PropertyName = "JOBS", ItemConverterType = typeof(StringToJobsConverter))]
public List<JobClass> Jobs { get; set; }
I created the class, and created the conversion method inside it as follows:
return JsonConvert.DeserializeObject<List<JobClass>>(existingValue.ToString());
No luck. The error retuned is Could not cast or convert from System.String to System.Collections.Generic.List`1[AppNamespace.JobClass].
Breakpoints in the converter code are never hit. Can anyone see what I'm doing wrong?
UPDATE
I found the issue but don't know how to fix. The converter is being applied to the JobClass inside the list. Not to the List itself. I want the converter applied one time only to the List deserialization. Instead it's applied to each JobClass record inside the list.
string json = #"{str1:""abc"",list:""[1,2,3]"", str2:""def""}";
var temp = JsonConvert.DeserializeObject<Temp>(json);
public class Temp
{
public string str1;
[JsonConverter(typeof(StringConverter<List<int>>))]
public List<int> list;
public string str2;
}
public class StringConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject<T>((string)reader.Value);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

Categories