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

时间:2014-02-28 21:37:10

标签: c# .net json 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'但这似乎只适用于我反序列化为类/对象的情况,这是由于Feed返回的不同字段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...
    }
}

2 个答案:

答案 0 :(得分:2)

如果您已经解析了JSON,那么删除空值的唯一方法就是像您现在一样单独检查每个属性。另一种方法是在读取JSON时删除空值,因此您不必在以后过滤它们。 Json.Net不提供开箱即用的功能,但是可以编写一个直接与JsonReader一起工作的辅助方法来构建一个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() == "");
}

答案 1 :(得分:0)

感觉很冗长,但是我使用它来过滤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;
    }
}