如何干净地反序列化JSON,其中字符串值包装在同名对象中

时间:2014-02-13 08:19:53

标签: c# json json.net deserialization

我想将一些奇怪的JSON反序列化为C#类:

{
    "Result": {
        "Client": {
            "ProductList": {
                "Product": [
                    {
                        "Name": {
                            "Name": "Car polish"
                        }
                    }
                ]
            },
            "Name": {
                "Name": "Mr. Clouseau"
            },
            "AddressLine1": {
                "AddressLine1": "Hightstreet 13"
            }
        }
    }
}

json2csharp为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; }
}

问题是对象中的重复属性名称(Name中的ProductClient中的AddressLine1Client中的Name强迫我创建一个只有一个字符串属性(AddressLine1[JsonProperty(PropertyName = "Name")])的额外类,可以反序列化JSON。

生成的代码也无效,因为成员名称不能与其封闭类型相同(但我知道可以使用{{1}}属性解决)。

在类层次结构中避免不必要的级别并使用干净的类结构以便能够使用JSON.NET反序列化此JSON的最佳方法是什么?请注意,这是第三方API,因此我不能只更改JSON。

2 个答案:

答案 0 :(得分:2)

听起来您可能对实现自定义JsonConverter感兴趣。 Here's a site有一些如何做到这一点的样本。这是一个相当简单的过程,可以让你保持JSON你一直坚持,同时拥有你最熟悉的任何类结构。

答案 1 :(得分:2)

实际上,这是API结果的一种奇怪格式,使其更难以使用。解决该问题的一个想法是创建一个自定义JsonConverter,它可以获取包装值并返回内部值,就像包装器不在那里一样。这将允许您将笨重的JSON反序列化为更合理的类层次结构。

这是一个应该工作的转换器:

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();
    }
}

使用此转换器,您可以创建一个类层次结构,以消除额外的嵌套级别。您必须使用[JsonConverter]属性标记需要“解包”的属性,以便Json.Net知道何时应用自定义转换器。这是改进的类结构:

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; }
}

(请注意,如果Result对象不包含除Client之外的任何其他属性,您也可以在其中应用WrappedObjectConverter以将Client移至RootObject并删除Result类。)

以下是演示转换器的演示:

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);
    }
}

输出:

Car polish
Mr. Clouseau
Hightstreet 13