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.
NET objects (deserialize) A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.
JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).
Text. Json doesn't support the serialization of polymorphic type hierarchies. For example, if a property's type is an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties.
Provides methods for converting between . NET types and JSON types.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With