使用JSON.Net反序列化JSON时删除嵌套/数组

时间:2017-05-09 18:45:56

标签: c# json json.net

我遇到了一些问题,试图从我正在使用的API反序列化一些JSON。出于某种原因,API在没有必要的情况下喜欢将数据包装在额外的层和数组中。

{
    "CustomData": [
        {
            "Wrapper": [
                {
                    "OptionalDataSet1": [
                        {
                            "ItemA": "Basic string"
                        },
                        {
                            "ItemB": "Another basic string"
                        }
                    ]
                }
            ]
        }
    ]
}

使用json2csharp我已经获得了适用于上述示例的类。

public class OptionalDataSet1
{
    public string ItemA { get; set; }
    public string ItemB { get; set; }
}

public class Wrapper
{
    public List<OptionalDataSet1> OptionalDataSet1 { get; set; }
}

public class CustomData
{
    public List<Wrapper> Wrapper { get; set; }
}

public class RootObject
{
    public List<CustomData> CustomData { get; set; }
}

我遇到的问题是&#34; Wrapper&#34;根据API,类是不需要的。它始终在那里,但它将是自定义数据中唯一的项目。此外,只有一个&#34; OptionalDataSet1&#34;的实例。在包装内。还有其他&#34; OptionalDataSets&#34;,但同样,它们对于每个请求都是唯一的。

最后,Wrapper为&#34; OptionalDataSet1&#34;反序列化两个对象,第一个具有ItemA&#39; s值,第二个具有ItemBs。还有其他数据集可以拥有超过40个项目,我不想扫描对象的40个实例,以找出哪个具有我试图找到的单个数据属性。< / p>

我是否应该通过删除&#34; Wrapper&#34;来按下我从API接收的JSON字符串,然后再发送它以进行反序列化。并转换List&lt;&gt;单数实例的属性,或者是否有其他方法我使用JSON.Net来生成像

这样的东西
RootObject.CustomData.OptionalDataSet1.ItemB

而不是

RootObject.CustomData[0].Wrapper[0].OptionalDataSet[1].ItemB

3 个答案:

答案 0 :(得分:1)

听起来你真的只对JSON中最里面的数组(OptionalDataSet1)内的键值对感兴趣,而这些键可能因你所拥有的数据集而异。从API请求。在这种情况下,我将使用LINQ-to-JSON API创建一个帮助方法来解析JSON并在Dictionary<string, string>中返回所需的数据。然后,您不必担心为所有不同的数据集定义不同的类。

public static Dictionary<string, string> Deserialize(string json)
{
    return JObject.Parse(json)
        .SelectToken("CustomData[0].Wrapper[0].OptionalDataSet1")
        .Children<JObject>()
        .SelectMany(jo => jo.Properties())
        .ToDictionary(jp => jp.Name, jp => (string)jp.Value);
}

然后你可以像这样反序列化:

Dictionary<string, string> optionalDataSet1 = Deserialize(json);

从那里,您可以轻松访问您感兴趣的任何项目:

string itemA = optionalDataSet1["ItemA"];

或者您可以转储所有键值对,如下所示:

foreach (var kvp in optionalDataSet1)
{
    Console.WriteLine(kvp.Key + ": " + kvp.Value);
}

小提琴:https://dotnetfiddle.net/6ekOFp

答案 1 :(得分:0)

好吧,虽然我非常分心,因为你确定API总会在数组中返回1个实例,但我会回答它应该完成而不是通过json进行黑客攻击。

主要原因是从实际数据对象(DTO)中抽象出您的实现。 因此,对于您生成的类 - 只需将它们留下即可。它是您的API的传输协议,而不是您的业务逻辑范围,因此您最好不要以任何方式触摸它。只要相信我,协议中的每一个变化都会触及你的业务部分 - 这不好,每次有人想在前端更改API时,都会导致不必要的开发人员时间消耗。

它只是生成代理类,它可以很容易地重新生成,你不​​能改变它们。 API可以更改,您的代码应该完好无损。

而不是这些黑客只是将它们映射到你心中想要的结构中:

public class MyApiResponse
{
     public string ItemA {get;set;}
     public string ItemB {get;set;}
}

var n = new MyApiResponse
{
    ItemA = ...,
    ItemB = ...
}

答案 2 :(得分:0)

嗯,解决方案最终是编写我自己的dbson

所提到的JsonConverter
public override object ReadJson(JsonReader reader, Type objectType, 
                                object existingValue, JsonSerializer serializer)
{
    JObject jo = JObject.Load(reader);
    object targetObj = Activator.CreateInstance(objectType);

    foreach (PropertyInfo prop in objectType.GetProperties()) {
        string jsonPath = prop.Name;
        JToken token = jo.SelectToken(jsonPath);

        if (jsonPath.Equals("OptionalDataSet1")) {
            var innerItems = jo.SelectToken("Wrapper[0]").SelectToken(jsonPath).Values();
            var finalItem = new JObject();
            foreach (var item in innerItems) {
                var tempItem = new JObject(item);
                finalItem.Merge(tempItem);
            }
        } else {
            token = jo.SelectToken(jsonPath).First();
        }

        if (token != null && token.Type != JTokenType.Null) {
            object value = token.ToObject(prop.PropertyType, serializer);
            prop.SetValue(targetObj, value, null);
        }
    }

    return targetObj;
}

使用这个自定义转换器,我能够删除对Wrapper类的需求,并且具有OptionalDataSet1和CustomData的单个实例,并且OptionalDataSet1的单个实例正确地填充了所有它的信息。

(代码可能已关闭,即时从VB.Net转换为此答案)