使用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
相关产品推荐
相关产品推荐

