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

dom*_*enu 6 c# json json.net deserialization

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

{
    "Result": {
        "Client": {
            "ProductList": {
                "Product": [
                    {
                        "Name": {
                            "Name": "Car polish"
                        }
                    }
                ]
            },
            "Name": {
                "Name": "Mr. Clouseau"
            },
            "AddressLine1": {
                "AddressLine1": "Hightstreet 13"
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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; }
}
Run Code Online (Sandbox Code Playgroud)

问题是,在对象复制的属性名称(NameProductClient,AddressLine1Client)迫使我只有一个字符串属性(创建一个额外的类Name,AddressLine1)要能够反序列化JSON的.

生成的代码也无效,因为成员名称不能与其封闭类型相同(但我知道可以使用该[JsonProperty(PropertyName = "Name")]属性解决).

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

Bri*_*ers 3

事实上,对于 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个转换器,您可以创建一个类层次结构,消除额外的嵌套级别。您必须使用属性标记需要“解包”的属性,[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; }
}
Run Code Online (Sandbox Code Playgroud)

(请注意,如果该Result对象除 之外不包含任何其他属性,您也Client可以应用那里将向上移动到并消除该类。)WrappedObjectConverterClientRootObjectResult

下面是一个展示转换器实际运行情况的演示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Car polish
Mr. Clouseau
Hightstreet 13
Run Code Online (Sandbox Code Playgroud)