Parsing versioned JSON - c#

I have objects represented by two different Jsons:
JSON v1:
{
"names":[{"name":John},{"name":Eve}],
"version": 1
}
JSON v2:
{
"names":[{"name":"John", "age":22},{"name":"Eve", "age":35}],
"version": 2
}
Now I have class which can parse the first version of Json:
[JsonObject("Class")]
public class PeopleEntity
{
public string name{ get; set; }
}
public class People
{
public List<PeopleEntity> names{ get; set; }
public string version{ get; set; }
}
And this is how it is parsed:
result = JsonConvert.DeserializeObject<People>(json_data, new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore
});
Now, the problem is, that the request for parsing the second version of JSON came. What would be the best solution to handle this?
I have an idea to make list of interfaces (because of the attribute name, which is always there) and make multiple implementations of it, one for each version.

I don't see any need for interfaces to solve this.
If you want to deserialize both versions of JSON into the same type you could add a nullable age property to PersonEntity like this:
[JsonObject("Class")]
public class PeopleEntity {
public string name { get; set; }
public int? age { get; set; }
}
Notice the ? following the int type which means that property can be null. Using Json.NET, if you deserialize v1 JSON into this class the name will be populated as expected and the age will remain null.
Alternatively, since you know which version of JSON is being deserialized up front, you could have a different type for each version. Use you're existing PeopleEntity type for v1 and create a derived class that adds an age property for v2.

Related

json.net to System.text.json desiration for nested class in .net 5

I am having issue.I have migrated Json.net to system.text.json.
Json serialization and deserialization of non nested simple class types are working fine..
Json Request:
{"args":{"pageIndex":1,"pageSize":10,"filteringOptions":[],"sortingOptions":[{"field":"ain","direction":"ASC"}]}}
For the below serialization of complex types are getting error..
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter()); // Input/Output date format
});
Class:
public class SearchPageRequest
{
public PageSearchArgs Args { get; set; } = new PageSearchArgs();
}
public class PageSearchArgs
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public PagingStrategy PagingStrategy { get; set; }
public List<SortingOption>? SortingOptions { get; set; } //Only List are not mapping
public List<FilteringOption>? FilteringOptions { get; set; }
}
public class SortingOption
{
public string Field { get; set; } = null!;
//[JsonPropertyName("direction")] tested put Json property for all propertie
public SortingDirection Direction { get; set; }
public int Priority { get; set; }
public enum SortingDirection
{
ASC,
DESC
}
}
i am using default case in C# Class ( Pascal case) and in angular interface ( Camelcase)..
in .net 5 and angular 10 application.
Error In model validation.
Key: "$.args.sortingOptions[0].direction" (Camelcase)
Value:The JSON value could not be converted to Application.Wrappers.Paging.SortingOption+SortingDirection. Path: $.args.sortingOptions[0].direction | LineNumber: 0 | BytePositionInLine: 109.
Please let me know..i am trying from 2 days..The data coming from angular slickdrid..if any changes i have to modify in c# class itlsef
What i have tried: (It is working fine in json.net)
Json property name i have defined as camelcase
Based on error
$.args.sortingOptions[0].direction is not mapping to SortingOptions of direction.which is enum property..Please let me know alternate solution?
Do you really need to migrate Json.net to system.text.json?
According to https://visualstudiomagazine.com/articles/2020/07/28/json-serializers.aspx
If you're already using Newtonsoft.Json in an existing project, you
likely don't need to switch. If you absolutely need high JSON
serialization/deserialization performance, go with System.Text.
Json.net has a few convenient features, that are not available in System.Text. It is better to use the package that is more suitable for the job.

JSON.NET: How to deserialize from specific json object to a class with a different name

This is driving me kinda nuts, because we don't have time to ask for the API team to change their response object name.
We have a json results that reads:
"seqRing": {
"admStatus": "disabled",
"status": null,
"lines": null
},
And I need that the json deserialization map it to this class:
public class SequentialRing
{
public string admStatus { get; set; }
public string status { get; set; }
public List<SeqLinesAttr> SeqLines { get; set; } = new List<SeqLinesAttr>();
}
If this were a case of a difference in the property name, I could simply use the JsonPropertyAttribute at the top of the property, but I need something similar for the class.
If there's something I could use? Please.
Thank you!
The JSON that you've shown has "seqRing" as a property of a larger JSON object. In order to deserialize that, you can create a wrapper class (or using an existing class) where you can specify the JSON property name for the class:
public class SequentialRingWrapper
{
[JsonProperty("seqRing")]
public SequentialRing SequentialRing { get; set; }
}

Newtonsoft json is not deserializing when the name of the class is the root element

Newtonsoft json DeserializeObject is not parsing the json message when the name of the class is the root element.
var json = " {\r\n \"amount\": {\r\n \"currency\": \"EUR\",\r\n \"value\": 99792\r\n }\r\n}";
var amount = JsonConvert.DeserializeObject<Amount>(json)
and the class
class Amount
{
[JsonProperty("value")]
public decimal? Value { get; set; }
[JsonProperty("currency")]
public string Currency { get; set; }
}
In that case the Amount properties are null. The problem is that the amount is nested in a more complex json and I found out that it is always returned empty because it starts with the "amount". Of course I tried some annotations in the Amount class like [DataContract] and [JsonObject] but still it is empty
In case of:
var json = "{\r\n \"currency\": \"EUR\",\r\n \"value\": 99792\r\n }";
Then is parsed properly. The question is how can I deseriale the json in the first case?
You could create a wrapping class Root, which has a single element called Amount. For example,
public class Root
{
[JsonProperty("amount")]
public Amount Amount { get; set; }
}
You would now need to deserialize to an instance of Root. For example,
var amount = JsonConvert.DeserializeObject<Root>(json);
Alternatively, if you do not want to declare another class, you could also use
var innerJson = JObject.Parse(json)["amount"].ToString();
var amount = JsonConvert.DeserializeObject<Amount>(innerJson);
You should model your classes like this:
public class Amount
{
[JsonProperty("value")]
public decimal? Value { get; set; }
[JsonProperty("currency")]
public string Currency { get; set; }
}
public class RootObject
{
[JsonProperty("amount")]
public Amount Amount { get; set; }
}
Then deserialize RootObject:
var amount = JsonConvert.DeserializeObject<RootObject>(json);
Note: You can paste your JSON into json2csharp.com, which models your JSON into C# classes for you.
how can I deseriale the json in the first case
Reason why your json is not getting deserialized correctly is because Newtonsoft does not know how to translate amount to your class that has two variables, named: currency and value. To deserialize the json in the first case, you need a class for the first object: amount. This object contains another object that matches up with the class you were working with, Amount.
When you add the missing class, RootObject, and deserialize to that RootObject, you will see the json deserialize correctly.
Classes you need
public class Amount
{
[JsonProperty("currency")]
public string Currency { get; set; }
[JsonProperty("value")]
public int Value { get; set; }
}
public class RootObject
{
[JsonProperty("amount")]
public Amount Amount { get; set; }
}
Use this for Deserialization
var amount = JsonConvert.DeserializeObject<RootObject>(json)
Easy way to find the classes
I have found www.json2csharp.com to be a pretty good resource. Whenever in doubt, copy / paste your json there to get all the classes you need. Copy them in your project and deserialize to RootObject. If your JSON is invalid, you'll find out whats wrong there as well.

Is it possible to deserialize JSON with an unknown parameter name without using a custom converter?

I apologize if this is a duplicate, but none of the related answers I saw had JSON similar to the format I'm dealing with. I'm consuming an API response that has the following JSON format:
{
"KnownPropName": [
{
"DynamicPropName": [
{
"KnownProp1": "value",
"KnownProp2": "value"
},
{
"KnownProp1": "value",
"KnownProp2": "value"
}]
}]
}
I know the names of the parameters for each object except for "DynamicPropName", which I cannot know ahead of time.
I have the below classes setup:
private class TestResponse
{
public TestOuter[] KnownPropName { get; set; }
}
private class TestOuter
{
public TestInner[] TestInners { get; set; }
}
private class TestInner
{
public string KnownProp1 { get; set; }
public string KnownProp2 { get; set; }
}
If I change the property name "TestInners" to "DynamicPropName" everything deserializes with no issues. However, since I will not know the actual property name ahead of time I need to somehow have this work when "TestInners" does not match the corresponding property name.
I don't believe I can use a dictionary because the arrays don't contain string keys, but instead objects.
I know I can use a custom converter to solve this, but I'd like to know if it is at all possible without using one. Is there a way to have JSON deserialize based on the order of the parameters instead of depending on the name?
From the JSON sample you've given, I don't see why a dictionary wouldn't work here. As I see it, KnownPropName is actually an array of dictionaries where each dictionary has string keys, representing the dynamic property name(s), and values which are arrays of TestInner objects.
In other words, declare your classes like this:
private class TestResponse
{
public Dictionary<string, TestInner[]>[] KnownPropName { get; set; }
}
private class TestInner
{
public string KnownProp1 { get; set; }
public string KnownProp2 { get; set; }
}
You don't need the TestOuter class.
Fiddle: https://dotnetfiddle.net/MHOYwm

Questioning received JSON structure

I'm currently using a beta API (http://developer.riotgames.com/api/methods) which returns JSON for all the exposed methods.
I've been able to use JSON.NET to deserialize all of these return values so far. However, today I consumed one of their function which returns a JSON that is valid but is in my opinion not correct.
You're probably wondering, why don't you ask it on the beta forum? I have but I haven't received an answer so far and in general this intrigues me.
A snippet of the JSON return:
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<...
}
The problem I have with this structure is that the ID is used as a "group" without an identifier. I would be able to use this decently if it had
"ItemID" : "1001"
But it doesn't have that. I don't mind manually parsing it but I'd first like to know whether or not this JSON is correct (not just valid).
Do you agree that this is not a clean way of creating a JSON block that contains a list of elements or am I missing something here? So far I haven't seen any comments on the beta forum of this API so I'm really wondering why.
Edit "valid" vs "correct/usable":
I know it's a valid JSON statement. I'm questioning the fact whether this is usable with JSON.NET.
I have the following class definition (with two subclasses):
public class JSONItem
{
[JsonProperty("tags")]
public string[] Tags { get; set; }
[JsonProperty("plaintext")]
public string Plaintext { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("into")]
public string[] Into { get; set; }
[JsonProperty("image")]
public JSONItemImage Image { get; set; }
[JsonProperty("colloq")]
public string Colloq { get; set; }
[JsonProperty("gold")]
public JSONItemGold Gold { get; set; }
}
When giving the above JSON block to to JSONConvert.DeserializeObject(json) it throws an error because "1001" is not mentioned in JSONItem.
How do you handle this so that you can use JSON.NET?
A class like this won't work because you have no names to give the properties:
public class JSONItemWrapper
{
[JsonProperty("")]
public string ID { get; set; }
[JsonProperty("")]
public JSONItem MyProperty { get; set; }
}
Edit: "consistent with other methods"
The other methods return blocks where every property is within {} and has an identifier. The most recently added function have this "primary key outside of {}" style.
It is a valid json and you can use a type like Dictionary<string, SomeObject> to deserialize your json.
string json = #"{
""1001"": {
""name"": ""Boots of Speed"",
""plaintext"": ""Slightly increases Movement Speed"",
""group"": ""BootsNormal"",
""description"": ""desc...""
}
}";
var dict = JsonConvert.DeserializeObject<Dictionary<string, MyObject>>(json);
and accesing an item later on by its key can be fast too.
public class MyObject
{
public string name { get; set; }
public string plaintext { get; set; }
public string group { get; set; }
public string description { get; set; }
}
It's annoying when APIs do things like this (using numbers as property names), but all is not lost. Simply deserialize the JSON using Json.NET and then access each of the items using the indexer operator on the parent object.
EDIT:
I almost never create DTOs when deserializing JSON. It's lots of unnecessary boilerplate in most cases. I prefer deserializing to a dynamic object, but that won't be as effective when dealing with property names that begin with digits.
Here is how I would deserialize your sample message:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonExample
{
internal class Program
{
private static void Main()
{
const string json = #"
{
'1001': {
'name': 'Boots of Speed',
'plaintext': 'Slightly increases Movement Speed',
'group': 'BootsNormal',
'description': '<...'
}
}";
var jObject = JsonConvert.DeserializeObject<JObject>(json);
var plaintext = jObject["1001"]["plaintext"].Value<string>();
Console.WriteLine(plaintext);
}
}
}
When put into http://JSONLint.com,
{
"1001": {
"name": "Boots of Speed",
"plaintext": "Slightly increases Movement Speed",
"group": "BootsNormal",
"description": "<..."
}
}
Validates as JSON.

Categories