首页 > 解决方案 > JRaw SelectToken 返回 null

问题描述

我在 .Net core 2.0 中使用 Newtonsoft.Json 11.0.2。

如果我使用 JObject,我会SelectToken喜欢这样:

JObject.Parse("{\"context\":{\"id\":42}}").SelectToken("context.id")

退货

42

但是,如果我使用 JRaw,我会为同一路径获得 null 吗?

new JRaw("{\"context\":{\"id\":42}}").SelectToken("context.id")

返回

空值

由于我的代码是如何设置的,我的模型已经在 J​​Raw 中,将它转换为 JObject 只是为了选择这个标记似乎浪费了 RAM(这个调用在热路径上)。

更新 好的,我的实际数据来自一个模型,其中只有一个属性是 JRaw,所以我需要类似下面的东西才能工作:

 JsonConvert.DeserializeObject<Dictionary<string, JRaw>>(
 "{\"a\":{\"context\":{\"id\":42}}}")["a"].SelectToken("context.id")

以上再次返回null。

标签: c#json.net

解决方案


标题可能有点误导,但基本上 OP 需要的是一种在JRaw不消耗太多内存的情况下解析现有(和大型)对象的方法。

我进行了一些测试,并且能够使用JsonTextReader.

我不知道 OP 的 json 字符串的确切结构,所以我假设是这样的:

[
  {
    "context": {
      "id": 10
    }
  },
  {
    "context": {
      "id": 20
    }
  },
  {
    "context": {
      "id": 30
    }
  }
]

结果将是一个具有 id 值 (10, 20, 30) 的整数数组。

解析方法

所以这是将JRaw对象作为参数并提取 Ids 的方法,使用JsonTextReader.

private static IEnumerable<int> GetIds(JRaw raw)
{
    using (var stringReader = new StringReader(raw.Value.ToString()))
    using (var textReader = new JsonTextReader(stringReader))
    {
        while (textReader.Read())
        {
            if (textReader.TokenType == JsonToken.PropertyName && textReader.Value.Equals("id"))
            {
                int? id = textReader.ReadAsInt32();

                if (id.HasValue)
                {
                    yield return id.Value;
                }
            }
        }
    }
}

在上面的示例中,我假设只有一种类型的对象具有 id 属性。

还有其他方法可以提取我们需要的信息 - 例如,我们可以检查令牌类型和路径,如下所示:

if (textReader.TokenType == JsonToken.Integer && textReader.Path.EndsWith("context.id"))
{
    int id = Convert.ToInt32(textReader.Value);
    yield return id;
}

测试代码

出于测试目的,我创建了以下与上述 json 结构匹配的 C# 类:

public class Data
{
    [JsonProperty("context")]
    public Context Context { get; set; }

    public Data(int id)
    {
        Context = new Context
        {
            Id = id
        };
    }
}

public class Context
{
    [JsonProperty("id")]
    public int Id { get; set; }
}

创建一个 JRaw 对象并提取 Id:

class Program
{
    static void Main(string[] args)
    {
        JRaw rawJson = CreateRawJson(); 
        List<int> ids = GetIds(rawJson).ToList(); 

        Console.Read();
    }

    //  Instantiates 1 million Data objects and then creates a JRaw object
    private static JRaw CreateRawJson()
    {
        var data = new List<Data>();

        for (int i = 1; i <= 1_000_000; i++)
        {
            data.Add(new Data(i));
        }

        string json = JsonConvert.SerializeObject(data);

        return new JRaw(json);
    }
}

内存使用情况

使用 Visual Studio 的诊断工具,我拍摄了以下快照,以检查内存使用情况:

内存使用截图

  • 快照 #1 是在控制台应用程序开始时拍摄的(正如预期的那样,内存不足)
  • 快照 #2 是在创建 JRaw 对象后拍摄的

    JRaw rawJson = CreateRawJson();

  • 快照 #3 是在提取 ID 后拍摄的

    列表 ids = GetIds(rawJson).ToList();


推荐阅读