JSON.NET 解析集合时忽略空值



我正在通过API从我们的调查提供商中提取调查回复。我没有为此使用类来反序列化,它只是键/值对。

我正在从文件中读取一系列响应。每个响应都包含许多我们称之为"响应项",如下所示:

[
    {
        "response_id":"1234",
        "hRz5aMmGPf": null,
        "6UnnAZSEBT": null,
        "nGS1cyvLwK": "Red"
    },
    {
        "response_id":"1235",
        "hRz5aMmGPf": "John Smith",
        "6UnnAZSEBT": null,
        "nGS1cyvLwK": "Blue"
    },
    {
        "response_id":"1236",
        "hRz5aMmGPf": "Jane Doe",
        "6UnnAZSEBT": "Yes",
        "nGS1cyvLwK": null
    }
]

出于本练习的目的,我从文件中读取 JSON,如下所示:

List<JToken> responseobjs = new List<JToken>();
JObject o = JObject.Parse(fcontents);
responseobjs.AddRange(o["results"].Children())

根据示例数据,源数据中有很多空值,我想以最快的方式消除这些空值。我已经阅读了"NullValueHandling",但这似乎仅适用于我反序列化为类/对象的情况,这在提要返回不同的字段 ID 时是不可能的。

我可以拿上面的 Children() 但跳过空值吗?

现在,我正在迭代响应,然后迭代响应项以删除空值,并且转换需要很长时间。

foreach (JToken obj in responseobjs)
{
    foreach (JProperty rprop in obj.Where(x=> x.HasValues==true).ToList())
    {
        if (rprop.Value.ToString().Trim() == "")
            continue;
        ..Continue parsing here...
    }
}

如果已经解析了 JSON,则删除 null 值的唯一方法是像您已经做的那样单独检查每个属性。 另一种方法是在读取 JSON 时删除 null,这样以后就不必筛选它们。 Json.Net 不提供现成的此功能,但可以编写一个直接与 JsonReader一起使用的帮助程序方法,以生成排除 null 值的JToken层次结构:

static JToken DeserializeExcludingNulls(string json)
{
    using (JsonTextReader reader = new JsonTextReader(new StringReader(json)))
    {
        return DeserializeExcludingNulls(reader);
    }
}
static JToken DeserializeExcludingNulls(JsonReader reader)
{
    if (reader.TokenType == JsonToken.None)
    {
        reader.Read();
    }
    if (reader.TokenType == JsonToken.StartArray)
    {
        reader.Read();
        JArray array = new JArray();
        while (reader.TokenType != JsonToken.EndArray)
        {
            JToken token = DeserializeExcludingNulls(reader);
            if (!IsEmpty(token))
            {
                array.Add(token);
            }
            reader.Read();
        }
        return array;
    }
    if (reader.TokenType == JsonToken.StartObject)
    {
        reader.Read();
        JObject obj = new JObject();
        while (reader.TokenType != JsonToken.EndObject)
        {
            string propName = (string)reader.Value;
            reader.Read();
            JToken token = DeserializeExcludingNulls(reader);
            if (!IsEmpty(token))
            {
                obj.Add(propName, token);
            }
            reader.Read();
        }
        return obj;
    }
    return new JValue(reader.Value);
}
static bool IsEmpty(JToken token)
{
    return (token.Type == JTokenType.Null);
}

这是一个演示:

string json = @"
[
    {
        ""response_id"":""1234"",
        ""hRz5aMmGPf"": null,
        ""6UnnAZSEBT"": null,
        ""nGS1cyvLwK"": ""Red"",
    },
    {
        ""response_id"":""1235"",
        ""hRz5aMmGPf"": ""John Smith"",
        ""6UnnAZSEBT"": null,
        ""nGS1cyvLwK"": ""Blue""
    },
    {
        ""response_id"":""1236"",
        ""hRz5aMmGPf"": ""Jane Doe"",
        ""6UnnAZSEBT"": ""Yes"",
        ""nGS1cyvLwK"": null
    }
]";
JArray array = (JArray)DeserializeExcludingNulls(json);
foreach (JObject obj in array)
{
    foreach (JProperty prop in obj.Properties())
    {
        Console.WriteLine(prop.Name + ": " + prop.Value);
    }
    Console.WriteLine();
}

输出:

response_id: 1234
nGS1cyvLwK: Red
response_id: 1235
hRz5aMmGPf: John Smith
nGS1cyvLwK: Blue
response_id: 1236
hRz5aMmGPf: Jane Doe
6UnnAZSEBT: Yes

如果还想排除空字符串,可以相应地更改IsEmpty方法:

static bool IsEmpty(JToken token)
{
    return (token.Type == JTokenType.Null) ||
           (token.Type == JTokenType.String && token.ToString().Trim() == "");
}
感觉

很长,但我用它来过滤掉 jsondiffpatch.net diff() 中的空值,所以我的单元测试通过(我的模型不序列化空值,但原始 json 可能包含空值)。

public static class JTokenExtensions
{
    public static JToken RemoveNulls(this JToken node)
    {
        var children = node.Children().ToArray();
        var nonTrivialChildren = children.Select(c => RemoveNulls(c)).Any(v => v != null);
        if (nonTrivialChildren)
            return node;
        // once trivial children are removed, values will be different
        var values = node.Values().ToArray();
        var nonTrivialValues = values.Any(v => v != null);
        if (nonTrivialValues)
            return node;
        // the parent needs to be removed instead
        switch (node.Type)
        {
            case JTokenType.Boolean:
            case JTokenType.Date:
            case JTokenType.Float:
            case JTokenType.Integer:
            case JTokenType.String:
                return node;
            case JTokenType.Array:
            case JTokenType.Object:
                return null;
            case JTokenType.Null:
                break;
        }
        node.Remove();
        return null;
    }
}

相关内容

  • 没有找到相关文章

最新更新