You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用System.Text.Json Utf8JsonReader时数组值重复读取的问题

自定义Utf8JsonReader反序列化时数组值被读取两次的问题

我尝试用Utf8JsonReader做自定义JSON反序列化(虽然常规反序列化很简单,但必须自定义实现)。反序列化包含对象的数组时,发现数组里的值被读了两次,截图如下:

截图显示数组值被读取两次

找不到太多Utf8JsonReader的使用资料,应该是我的实现有问题。以下是我的反序列化代码:

public Map? ReadYaml(string json)
{
    JsonReaderOptions options = new() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip };
    Utf8JsonReader reader = new(System.Text.Encoding.UTF8.GetBytes(json), options);

    Map map = new();

    while (reader.Read())
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.PropertyName:
                if (reader.ValueTextEquals("Map name"))
                {
                    reader.Read();

                    map = new(reader.GetString());
                }
                if (reader.ValueTextEquals("Entities"))
                {
                    reader.Read();

                    if (reader.TokenType == JsonTokenType.StartArray)
                    {
                        while (reader.Read())
                        {
                            if (reader.TokenType == JsonTokenType.EndArray)
                                break;

                            if (reader.TokenType == JsonTokenType.StartObject)
                            {
                                while (reader.Read())
                                {
                                    if (reader.TokenType == JsonTokenType.EndObject)
                                        break;

                                    if (reader.TokenType == JsonTokenType.PropertyName)
                                    {
                                        reader.Read();
                                        if (reader.TokenType == JsonTokenType.String)
                                        {
                                            var str = reader.GetString();
                                            if (str != null)
                                            {
                                                Debug.Log(str);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
                break;
        }
    }
    return map;
}

待反序列化的JSON内容:

{
  "Map name": "NewMap",
  "Entities": [
    {
      "Name": "Entity",
      "Active": "True",
      "GUID": "f06ed7d3-f1b3-4cfd-a623-a7ceb17dbced"
    },
    {
      "Name": "Entity #1",
      "Active": "True",
      "GUID": "03788f46-e430-4882-a097-e860d2c7aed5"
    }
  ],
  "Entity components": [
    {
      "Component type": "ExtremeEngine.Transform",
      "Component data": [
        {
          "Position": null,
          "Rotation": null,
          "Scale": null,
          "Entity": "f06ed7d3-f1b3-4cfd-a623-a7ceb17dbced",
          "GUIHeaderIsOpened": "True",
          "Enabled": "True",
          "GUID": "95dea154-6f00-41b0-bdec-fe78cca589c3"
        }
      ]
    },
    {
      "Component type": "ExtremeEngine.Transform",
      "Component data": [
        {
          "Position": null,
          "Rotation": null,
          "Scale": null,
          "Entity": "03788f46-e430-4882-a097-e860d2c7aed5",
          "GUIHeaderIsOpened": "True",
          "Enabled": "True",
          "GUID": "d92b131a-a82f-487f-b1c1-25ff13496284"
        }
      ]
    }
  ]
}

问题解决

你的代码主要有两个问题导致了数组值重复读取(或处理不完整)的现象:

1. 数组循环错误处理分隔符

在处理Entities数组时,当读取到元素之间的逗号(JsonTokenType.Comma),你的代码直接执行break退出数组循环,导致后续的数组元素无法被正确处理,反而被外层循环错误捕获,出现重复输出的假象。

正确的做法是遇到非StartObject的token时,用continue跳过,继续读取下一个token,直到遇到EndArray或StartObject。

2. 错误覆盖Map对象

处理"Map name"时,你直接new了一个新的Map对象,丢弃了之前初始化的实例,这可能导致后续对map的操作失效。应该改为给已有对象赋值属性。

修改后的完整代码:

public Map? ReadYaml(string json)
{
    JsonReaderOptions options = new() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip };
    Utf8JsonReader reader = new(System.Text.Encoding.UTF8.GetBytes(json), options);

    Map map = new(); // 保持这个实例,不要重新创建

    while (reader.Read())
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.PropertyName:
                if (reader.ValueTextEquals("Map name"))
                {
                    reader.Read();
                    // 给已有map对象赋值Name,而不是new新对象
                    map.Name = reader.GetString() ?? string.Empty;
                }
                else if (reader.ValueTextEquals("Entities")) // 用else if避免重复判断
                {
                    reader.Read();

                    if (reader.TokenType == JsonTokenType.StartArray)
                    {
                        while (reader.Read())
                        {
                            if (reader.TokenType == JsonTokenType.EndArray)
                                break;

                            // 跳过逗号等非对象开始的token,继续读取
                            if (reader.TokenType != JsonTokenType.StartObject)
                                continue;

                            // 处理单个Entity对象
                            Entity entity = new();
                            while (reader.Read())
                            {
                                if (reader.TokenType == JsonTokenType.EndObject)
                                {
                                    map.Entities.Add(entity); // 假设Map有Entities集合
                                    break;
                                }

                                if (reader.TokenType == JsonTokenType.PropertyName)
                                {
                                    string propName = reader.GetString() ?? string.Empty;
                                    reader.Read();
                                    if (reader.TokenType == JsonTokenType.String)
                                    {
                                        string value = reader.GetString() ?? string.Empty;
                                        Debug.Log($"{propName}: {value}");
                                        // 根据属性名给Entity赋值
                                        switch (propName)
                                        {
                                            case "Name":
                                                entity.Name = value;
                                                break;
                                            case "Active":
                                                entity.Active = bool.Parse(value);
                                                break;
                                            case "GUID":
                                                entity.GUID = Guid.Parse(value);
                                                break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                break;
        }
    }
    return map;
}

关键修改点:

  • 将处理"Map name"和"Entities"的两个if改为if-else if,避免同一token被多次判断
  • 数组循环中遇到非StartObject的token时用continue跳过,而不是break
  • 不再重新创建Map对象,而是给已有实例赋值属性
  • 添加了Entity对象的创建和赋值逻辑,确保反序列化的数据被正确保存到Map中

这样修改后,数组内的每个对象都会被正确处理一次,不会出现重复读取或处理不完整的情况。

内容的提问来源于stack exchange,提问作者oscar7070

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:12:34