繁体   English   中英

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

[英]Remove nesting/arrays when deserializing JSON using 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; }
}

根据API,我遇到的问题是不需要“包装器”类。 它始终存在,但是将成为“自定义数据”中的唯一项。 此外,包装器内部将永远只有一个“ OptionalDataSet1”实例。 还有其他“ OptionalDataSets”,但同样,每个请求它们都是唯一的。

最后,包装程序为“ OptionalDataSet1”反序列化两个对象,第一个对象具有ItemA的值,第二个对象具有ItemBs。 还有其他数据集可以使用多达40个项目,我不想浏览一个对象的40个实例来查找哪个实例具有我要查找的单个数据属性。

我是否应该先删除从API接收到的JSON字符串,然后再通过删除“包装器”并将List <>属性转换为单个实例进行反序列化,否则是否存在使用JSON.Net丢失的另一种方法产生类似

RootObject.CustomData.OptionalDataSet1.ItemB

代替

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

听起来您真的只对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

好吧,尽管Im使您确信API将始终返回数组中的1个实例这一事实使我非常分心,但我会回答说应该这样做,而不是通过json进行黑客攻击。

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

它只是生成的代理类,可以轻松地重新生成,您不得更改它们。 可以更改API,并且您的代码应该完整无缺。

除了这些hack之外,还可以将它们映射到您内心渴望的任何结构中:

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

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

好了,该解决方案最终就是编写我自己的dbc提到的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即时转换为该答案)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM