Parsing both Array and single object - c#

API retuned json object like below 2 forms.
Form 1
{
"Pricing": [
{
"total": 27,
"currency": "USD",
"charges": [ //Chargers Array
{
"code": "C1",
"currency": "USD",
"rate": 15
},
{
"code": "C45",
"currency": "USD",
"rate": 12
}
]
}
]
}
Form 2
{
"Pricing": [
{
"total": 12,
"currency": "USD",
"charges": { //Chargers single object
"code": "C1",
"currency": "USD",
"rate": 12
}
}
]
}
As you can see sometime chargers object return with array and some times not. My question is how to parse this to C# class object? If I added the C# class like below it cannot be parse properly for Form 2. (Form 1 parsing properly)
public class Charge
{
public string code { get; set; }
public string currency { get; set; }
public decimal rate { get; set; }
}
public class Pricing
{
public decimal total { get; set; }
public string currency { get; set; }
public List<Charge> charges { get; set; } //In Form 2, this should be single object
}
public class MainObj
{
public List<Pricing> Pricing { get; set; }
}
Error occurred when parse with Newtonsoft deserialization.
MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);
Error
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'System.Collections.Generic.List`1[Charge]' because the type
requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix
this error either change the JSON to a JSON array (e.g. [1,2,3]) or
change the deserialized type so that it is a normal .NET type (e.g.
not a primitive type like integer, not a collection type like an array
or List) that can be deserialized from a JSON object.
JsonObjectAttribute can also be added to the type to force it to
deserialize from a JSON object. Path 'Pricing[0].charges.code', line
1, position 69.
Any common method for parsing, when receiving different type of object types with C#?
(I look into this as well but it's for java. And most of this kind of question raised for java but not C#.)

Yet another way of dealing with this problem is to define a custom JsonConverter which can handle both cases.
class ArrayOrObjectConverter<T> : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return token.Type == JTokenType.Array
? token.ToObject<List<T>>()
: new List<T> { token.ToObject<T>() };
}
public override bool CanConvert(Type objectType)
=> objectType == typeof(List<T>);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=>throw new NotImplementedException();
}
Inside the ReadJson first we get a JToken to be able to determine the read value's Type (kind)
Based on that the we can either call ToObject<List<T>> or ToObject<T>
Inside the CanConvert we examine that the to be populated property's type is a List<T>
Even though there is a generic JsonConverter<T> where you don't have to define the CanConvert, its ReadJson can be implemented in a bit more complicated way
Since the question is all about deserialization I've not implemented the WriteJson method
You might also consider to override the CanWrite property of the base class to always return false
With this class in our hand you can decorate your properties with a JsonConverterAttribute to tell to the Json.NET how to deal with those properties
public class Pricing
{
public decimal total { get; set; }
public string currency { get; set; }
[JsonConverter(typeof(ArrayOrObjectConverter<Charge>))]
public List<Charge> charges { get; set; }
...
}

You could go for an approach to try and parse Form1's object to json, if it fails it will use Form2's object to json.
Example here and below: https://dotnetfiddle.net/F1Yh25
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string form1 = "{\"Pricing\":[{\"total\":27,\"currency\":\"USD\",\"charges\":[{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":15},{\"code\":\"C45\",\"currency\":\"USD\",\"rate\":12}]}]}";
string form2 = "{\"Pricing\":[{\"total\":12,\"currency\":\"USD\",\"charges\":{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":12}}]}";
string json = form1;//Change form1 to form2 and it will also work
try
{
Form1.Root Object = JsonConvert.DeserializeObject<Form1.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
catch//If Form1's json is Form2 it will catch here
{
Form2.Root Object = JsonConvert.DeserializeObject<Form2.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
}
public class Form1
{
public class Charge
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public List<Charge> charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
public class Form2
{
public class Charges
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public Charges charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
}

Ok so here is another way to do this without having to use two classes and not having a try catch. Basically just update Pricing class to following and it works for both cases. Probably a better way but this is better (at least in my opinion) and having two classes and having try catch do your branching. If you had ten properties with this issue would you then create 10! classes to handle every combo? No way lol!
public class Pricing {
public int total { get; set; }
public string currency { get; set; }
private List<Charge> _charges;
public object charges {
get {
return _charges;
}
set {
if(value.GetType().Name == "JArray") {
_charges = JsonConvert.DeserializeObject<List<Charge>>(value.ToString());
}
else {
_charges = new List<Charge>();
var charge = JsonConvert.DeserializeObject<Charge>(value.ToString());
_charges.Add(charge);
}
}
}
}

Related

Cannot Deserialize JSON Object [duplicate]

This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 4 years ago.
My problem is that I'm getting this error
"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[api.Controllers.Journey]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly" ... When I try to serialize my API json response.
The API return something like this:
{
"Transport": [
{
"$id": "6",
"SourceID": "1",
"Context": "1",
"Id": "AMADEUS#1",
"Provider": null,
"Journey": {
"SourceID": "R1G0S0",
"Duration": "42000",
"Id": "5b6db9c6bfac4"
}
},
{
"$id": "7",
"SourceID": "1",
"Context": "1",
"Id": "AMADEUS#1",
"Provider": null,
"Journey": [
{
"SourceID": "R1G0S0",
"Duration": "42000",
"Id": "5b6db9c6bfac4"
},
{
"SourceID": "R1G0S1",
"Duration": "42000",
"Id": "5b6db9c6bsac4"
}
]
}
]
}
The Journey field is an JObject at first result, but is an JArray on second...
And I'm getting the error when I deserialize:
Transport Transport = JsonConvert.DeserializeObject<Transport>(json_response);
My class properties:
public class Transport{
public string SourceID { get; set; }
public string Context { get; set; }
public string Id { get; set; }
public ProviderOD Provider { get; set; }
public Journey[] Journey { get; set; }
public PriceOD Price { get; set; }
}
public class Journey
{
public string Id { get; set; }
public string SourceID { get; set; }
public string Duration { get; set; }
}
What should I do, to set Journey[ ] or Journey dynamically ??
Thanks in advance ...
Your class declaration is not right, you have to use the following to decode
TransportRootObject Transport = JsonConvert.DeserializeObject<TransportRootObject>(json_response);
and this class(es) declaration
public class TransportRootObject
{
public List<Transport> Transport { get; set; }
}
public class Transport
{
[JsonProperty("$id")]
public string Id { get; set; }
public string SourceID { get; set; }
public string Context { get; set; }
public string Id { get; set; }
public ProviderOD Provider { get; set; }
public List<Journey> Journey { get; set; }
}
public class Journey
{
public string Id { get; set; }
public string SourceID { get; set; }
public string Duration { get; set; }
}
I think your issue is that you've got poorly formatted JSON being returned from your API. If Journey is an array, it should always return as an array/list...not an object if there is only 1 and then an array if there is more than one.
If this is a third party API that you are unable to fix then your next option is to use a custom converter to handle weirdly formatted json like this.
I don't recall where i found the original source/solution for this but I'll walk you through the steps.
First you'll need to create a new class such as this.
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
This is your custom converter to handle a "sometimes array, sometimes object" situation. It pretty much just does a test to determine if its an array or an object and handles accordingly.
Then, within your transport class, you'll add the attribute above the Journey array property like so.
[JsonConverter(typeof(SingleOrArrayConverter<Journey>))]
public List<Journey> Journey { get; set; }
This should then handle situations where journey comes through as an array or as an object.
So there's a few issues here. Your top level Transport object in your JSON is actually an array of transport objects. So you need an extra class:
public class DocumentRoot
{
public List<Transport> Transport { get; set; }
}
Then you want:
var DocumentRoot = JsonConvert.DeserializeObject<DocumentRoot>(json_response);
//DocumentRoot.Transport is now a List<Transport> of your transport elements from the document.
In your JSON one of the member names is "$id" which of course isn't valid in C#, so to make this behave you need to add a [JsonProperty(PropertyName = "$id")] attribute on your Id property to tell the serializer the name.

Deserialize Dictionary with unknown keys with RestSharp

I currently am getting a JSON object with a shape similar to the following:
{
more data here...
"years": {
"value": 2013,
"item1": {
"total": 0.1044,
"Low": 0.0143,
"Mid": 0.1044,
"High": 0.3524,
"min": 0.0143,
"max": 0.3524,
},
"item2": {
"total": 0.1702,
"Low": 0.167,
"Mid": 0.1702,
"High": 0.1737,
"min": 0.167,
"max": 0.1737,
},...
}
}
I unfortunately, have no control over the shape of the JSON.
I am trying to get RestSharp to deserialize this into an object where Item1, Item2, and the rest fill into a Dictionary I currently have the following code:
public class Year
{
public int Value { get; set; }
public Dictionary<string, Data> Data { get; set; }
}
public class Data
{
public decimal Total { get; set; }
public decimal Low { get; set; }
public decimal Mid { get; set; }
public decimal High { get; set; }
public decimal Min { get; set; }
public decimal Max { get; set; }
}
And I am hoping to get Item1, Item2, etc. to be the keys of the Dictionary and the values underneath to fill in the Data class as the value of the Dictionary. But it isn't working at the moment (the rest of my structure is, it's just this innermost part). Am I just approaching the structure wrong? I want to avoid having to create a specific class for Item1 and Item2.
You could use a custom JsonConverter:
public class YearConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Year);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = JObject.Load(reader);
int year = (int)obj["value"];
var data = new Dictionary<string, Data>();
foreach (var dataItem in obj.Children()
.OfType<JProperty>()
.Where(p => p.Name.StartsWith("item")))
{
data.Add(dataItem.Name, dataItem.Value.ToObject<Data>());
}
return new Year
{
Value = year,
Data = data
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Decorate your Year class with the converter:
[JsonConverter(typeof(YearConverter))]
public class Year
{
public int Value { get; set; }
public Dictionary<string, Data> Data { get; set; }
}
Using it like this, for example:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""value"": 2013,
""item1"": {
""total"": 0.1044,
""Low"": 0.0143,
""Mid"": 0.1044,
""High"": 0.3524,
""min"": 0.0143,
""max"": 0.3524,
},
""item2"": {
""total"": 0.1702,
""Low"": 0.167,
""Mid"": 0.1702,
""High"": 0.1737,
""min"": 0.167,
""max"": 0.1737,
}
}";
var years = JsonConvert.DeserializeObject<Year>(json);
}
}
Steps:
1. Always shape your JSON object in a logical way
This issue would be significantly easier to handle if you restructured your json object in a more logical way. Right now you have n number of data objects at the same level as "value" and you are changing their property names dynamically.
Personally, I would reshape the json as such:
{
"years":[
{
"value":2013,
"data":[
{
"name":"item1",
"total":0.1044,
"Low":0.0143,
"Mid":0.1044,
"High":0.3524,
"min":0.0143,
"max":0.3524
},
{
"name":"item2",
"total":0.1702,
"Low":0.167,
"Mid":0.1702,
"High":0.1737,
"min":0.167,
"max":0.1737
}
]
}
]
}
Step 2: Add the Newtonsoft.Json attributes to your classes
This allows for easy deserialization
public class Base
{
[JsonProperty("years")]
public List<Year> years { get; set; }
}
public class Year
{
[JsonProperty("value")]
public int Value { get; set; }
[JsonProperty("data")]
public List<Data> data { get; set; }
}
public class Data
{
[JsonProperty("name")]
public string name { get; set; }
[JsonProperty("total")]
public decimal Total { get; set; }
[JsonProperty("Low")]
public decimal Low { get; set; }
[JsonProperty("Mid")]
public decimal Mid { get; set; }
[JsonProperty("High")]
public decimal High { get; set; }
[JsonProperty("min")]
public decimal Min { get; set; }
[JsonProperty("max")]
public decimal Max { get; set; }
}
Step 3: Deserialize it
Base myYears = JsonConvert.DeserializeObject<Base>(myJsonString);

Deserialize Json arrays and simple object

I´m deserializing a JSON response and there is a object that could be an array or a simple object, for example:
{
"ValueAdds":{
"#size":3,
"ValueAdd":[
{
"description":"desc 1"
},
{
"description":"desc 1"
}
]
}
}
and the other case:
{
"ValueAdds": {
"#size": "1",
"ValueAdd": {
"description": "Internet inalámbrico gratuito"
}
}
}
The entities:
public ValueAddsWrap
{
public ValueAddBase ValueAdds { set; get; }
}
public class ValueAddBase
{
public ValueAdd[] ValueAdd { set; get; }
}
public class ValueAdd
{
public string description { set; get; }
}
I´m getting an exception when I receive a simple object. How could I design the entities in order to deserialize an array and a simple object?
I´m using Newtonsoft:
T resp_object = JsonConvert.DeserializeObject<T>(json);
and the exception:
An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code
Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'rce.bookings.business.expedia.Responses.ValueAdd[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
You really can't do what I think you're asking. In the first case you've got a collection inside the object you're getting back.
public class ValueAdd
{
public string description { get; set; }
}
public class ValueAdds
{
public int size { get; set; }
public List<ValueAdd> ValueAdd { get; set; }
}
public class RootObject
{
public ValueAdds ValueAdds { get; set; }
}
In the second case you've got a single instance of an object.
public class ValueAdd
{
public string description { get; set; }
}
public class ValueAdds
{
public string size { get; set; }
public ValueAdd ValueAdd { get; set; }
}
public class RootObject
{
public ValueAdds ValueAdds { get; set; }
}
You might be able to get away with using a dynamic for the ValueAdd member, but that presents a whole bunch of annoying problems of its own as you'll still have to figure out whether it contains a collection or a single instance.
Realistically I see you have two options:
Option #1: Figure out if you've got the version with the collection or the single instance and deserialize into the appropriate type.
Option #2: Can you contact the author of the API and have them send back a consistent JSON structure? That's a lousy API if it changes the structure based on whether there are one or more than one ValueAdd objects.
With a JsonConverter:
public class ValueAddsConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var valueAdds = new ValueAdds();
var jo = (JObject)JObject.Load(reader)["ValueAdds"];
valueAdds.Size = Int32.Parse((string)jo["#size"]);
if (valueAdds.Size > 1)
{
valueAdds.ValueAdd = jo["ValueAdd"].Children().Select(x => x.ToObject<ValueAdd>());
}
else
{
valueAdds.ValueAdd = new List<ValueAdd>{jo["ValueAdd"].ToObject<ValueAdd>()};
}
return valueAdds;
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof (ValueAdds));
}
}
public class ValueAdd
{
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
[JsonConverter(typeof (ValueAddsConverter))]
public class ValueAdds
{
public int Size { get; set; }
public IEnumerable<ValueAdd> ValueAdd { get; set; }
}

newtonsoft.json deserializing logic

I'm trying to parse a json string with for example [1,2,3] to an array during deserializing.
This is my json data:
[
{
"id": "1",
"district": "1",
"lon": "4.420650000000000000",
"lat": "51.21782000000000000",
"bikes": "19",
"slots": "14",
"zip": "2018",
"address": "Koningin Astridplein",
"addressNumber": null,
"nearbyStations": "3,4,5,24",
"status": "OPN",
"name": "001- Centraal Station - Astrid"
}
]
And this is my c# currently mapping to a regular string, which i would like to be an array of integers.
var AvailabilityMap = new[] { new Station() };
var data = JsonConvert.DeserializeAnonymousType(json, AvailabilityMap);
public class Station
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
public string NearbyStations { get; set; }
public string Status { get; set; }
public string Name { get; set; }
}
I have found no way so far to do this in a proper way, without looping trough my current array once more..
Create a custom converter. Something like this:
public class StringToIntEnumerable : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override bool CanWrite
{
get
{
return false; // we'll stick to read-only. If you want to be able
// to write it isn't that much harder to do.
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Note: I've skipped over a lot of error checking and trapping here
// but you might want to add some
var str = reader.Value.ToString();
return str.Split(',').Select(s => int.Parse(s));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Now change you class to use the converter by using the JsonConverterAttribute:
public class Station
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
[JsonConverter(typeof(StringToIntEnumerable))]
public IEnumerable<int> NearbyStations { get; set; } // or List<int> or int[] if
// you prefer, just make
// sure the convert returns
// the same type
public string Status { get; set; }
public string Name { get; set; }
}
And now to deserialize:
var stations = JsonConvert.DeserializeObject<List<Station>>(json);
Here is a working fiddle that uses a custom JsonConverter.
What we're doing is converting your CSV values into a proper JSON array before we convert the entire JSON string into a Station object.
Custom JsonConverter
ReadJson reads through the JSON string. First, it loads the JSON into a JObject. Next, it gets the nearbyStations property and changes it from a simple CSV string into a JavaScript array. It does this by wrapping the CSV within square brackets. Finally, we use the JsonSerializer to populate our target object and return it.
CanWrite is set to false, because this JsonConverter is only allowed to read JSON not write to JSON. As a result, we don't need to implement WriteJson. The CanConvert tests to make sure that the target object is a Station.
public class StationConverter : JsonConverter
{
public override object ReadJson(
JsonReader r, Type t, object v, JsonSerializer s)
{
JObject jObject = JObject.Load(r);
var prop = jObject.Property("nearbyStations");
var wrapped = string.Format("[{0}]", prop.Value);
JArray jsonArray = JArray.Parse(wrapped);
prop.Value = jsonArray;
var target = new Station();
s.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter w, object v, JsonSerializer s)
{
throw new NotImplementedException("Unnecessary: CanWrite is false.");
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Station);
}
}
Change the Station Class to have an int[]
For the above JsonConverter to to work, change your NearbyStations property to be an int[].
public int[] NearbyStations
{
get;
set;
}
Usage Example with Live Fiddle
Here is an example of how to use it:
var AvailabilityMap =
JsonConvert.DeserializeObject<Station[]>(json, new StationConverter());
Console.WriteLine(AvailabilityMap[0].NearbyStations[0]);
The right answer is to have properly formatted input. In this case:
"nearbyStations": ["3","4","5","24"]
But I was faced with a similar situation in which the data could not be updated, so a solution had to be found. The easiest is to make a getter/setter that won't be touched by the serializer/deserializer. Unfortunately you can't ignore public properties with this serializer out of the box. So you have to do some clever work-arounds like reading into a DTO and using a business object for actual operations.
public class StationDTO
{
public int Id { get; set; }
public double Lon { get; set; }
public double Lat { get; set; }
public int Bikes { get; set; }
public int Slots { get; set; }
public string Address { get; set; }
public string NearbyStations { get; set; }
public string Status { get; set; }
public string Name { get; set; }
}
...
public class Station : StationDTO
{
public List<string> NearbyStationsList
{
get
{
return NearbyStations.Split(',');
}
set
{
NearbyStations = string.Join(",", value);
}
}
}
More information: Newtonsoft ignore attributes?

How to cleanly deserialize JSON where string values are wrapped in objects of the same name

I want to deserialize some strange JSON to C# classes:
{
"Result": {
"Client": {
"ProductList": {
"Product": [
{
"Name": {
"Name": "Car polish"
}
}
]
},
"Name": {
"Name": "Mr. Clouseau"
},
"AddressLine1": {
"AddressLine1": "Hightstreet 13"
}
}
}
}
json2csharp generates the following classes for the JSON:
public class Name
{
public string Name { get; set; }
}
public class Product
{
public Name Name { get; set; }
}
public class ProductList
{
public List<Product> Product { get; set; }
}
public class Name2
{
public string Name { get; set; }
}
public class AddressLine1
{
public string AddressLine1 { get; set; }
}
public class Client
{
public ProductList ProductList { get; set; }
public Name2 Name { get; set; }
public AddressLine1 AddressLine1 { get; set; }
}
public class Result
{
public Client Client { get; set; }
}
public class RootObject
{
public Result Result { get; set; }
}
The problem is that the duplicated property names in the objects (Name in Product and Client, AddressLine1 in Client) forces me to create an extra class with only one string property (Name, AddressLine1) to be able to deserialize the JSON.
The generated code is also invalid, because member names cannot be the same as their enclosing type (but I know that can be solved using the [JsonProperty(PropertyName = "Name")] attribute).
What's the best way to avoid that unnecessary level in the class hierarchy and have a clean class structure to be able to deserialize this JSON using JSON.NET? Note this is a third-party API, so I can't just change the JSON.
Indeed, this is a strange format for an API result, making it more difficult to consume. One idea to solve the problem is to create a custom JsonConverter that can take a wrapped value and return the inner value as if the wrapper were not there. This would allow you to deserialize the clunky JSON into a more sensible class hierarchy.
Here is a converter that should work:
class WrappedObjectConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
// Get the value of the first property of the inner object
// and deserialize it to the requisite object type
return token.Children<JProperty>().First().Value.ToObject(objectType);
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Armed with this converter, you can create a class hierarchy that eliminates the extra levels of nesting. You must mark the properties that need to be "unwrapped" with a [JsonConverter] attribute so that Json.Net knows when to apply the custom converter. Here is the improved class structure:
public class RootObject
{
public Result Result { get; set; }
}
public class Result
{
public Client Client { get; set; }
}
public class Client
{
[JsonConverter(typeof(WrappedObjectConverter))]
public List<Product> ProductList { get; set; }
[JsonConverter(typeof(WrappedObjectConverter))]
public string Name { get; set; }
[JsonConverter(typeof(WrappedObjectConverter))]
public string AddressLine1 { get; set; }
}
public class Product
{
[JsonConverter(typeof(WrappedObjectConverter))]
public string Name { get; set; }
}
(Note that if the Result object will not contain any other properties besides Client, you can apply the WrappedObjectConverter there as well to move the Client up to the RootObject and eliminate the Result class.)
Here is a demo showing the converter in action:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""Result"": {
""Client"": {
""ProductList"": {
""Product"": [
{
""Name"": {
""Name"": ""Car polish""
}
}
]
},
""Name"": {
""Name"": ""Mr. Clouseau""
},
""AddressLine1"": {
""AddressLine1"": ""Hightstreet 13""
}
}
}
}";
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
Client client = obj.Result.Client;
foreach (Product product in client.ProductList)
{
Console.WriteLine(product.Name);
}
Console.WriteLine(client.Name);
Console.WriteLine(client.AddressLine1);
}
}
Output:
Car polish
Mr. Clouseau
Hightstreet 13
It sounds like you may be interesting in implementing a custom JsonConverter. Here's a site that has some samples of how you could do this. It's a fairly simple process and would allow you to keep the JSON you're stuck with while having whatever class structure you're most comfortable with.

Categories