C#如何将键名为整数的JSON对象反序列化为数组
解决方案
不需要为0-5每个继电器单独定义冗余属性,通过自定义JSON转换器将动态数字键名的对象节点直接映射为固定长度数组,即可实现relayStatus[4]形式的索引访问效果。
第一步:定义精简版实体类
按照目标访问逻辑定义结构,去掉硬编码的Relay4这类冗余属性,可根据需要添加顶层快捷访问属性:
// 适用于.NET内置System.Text.Json,用Newtonsoft.Json的话把[JsonPropertyName]换成[JsonProperty]即可 public class DeviceData { [JsonPropertyName("slot")] public int Slot { get; set; } [JsonPropertyName("io")] public IoInfo Io { get; set; } // 顶层快捷访问,直接用data.RelayStatus[4]即可取值 public int[] RelayStatus => Io?.RelayStatusArray ?? new int[6]; } public class IoInfo { [JsonPropertyName("relay")] [JsonConverter(typeof(RelayArrayConverter))] public int[] RelayStatusArray { get; set; } = new int[6]; // 固定长度6,索引对应0-5继电器编号 } // 内部辅助类,用于匹配单个继电器的JSON结构 internal class SingleRelay { [JsonPropertyName("relayStatus")] public int Status { get; set; } }
第二步:实现自定义JSON转换器
.NET 5+/Core 内置System.Text.Json 版本
public class RelayArrayConverter : JsonConverter<int[]> { public override int[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var relayStatusArray = new int[6]; if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Relay节点格式非法,预期为对象类型"); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) return relayStatusArray; if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException("读取继电器编号失败"); string keyName = reader.GetString()!; // 键名不是合法0-5数字的直接跳过 if (!int.TryParse(keyName, out int relayIndex) || relayIndex < 0 || relayIndex > 5) { reader.Skip(); continue; } reader.Read(); var relayData = JsonSerializer.Deserialize<SingleRelay>(ref reader, options); relayStatusArray[relayIndex] = relayData.Status; } return relayStatusArray; } public override void Write(Utf8JsonWriter writer, int[] value, JsonSerializerOptions options) { writer.WriteStartObject(); for (int i = 0; i < value.Length; i++) { writer.WritePropertyName(i.ToString()); writer.WriteStartObject(); writer.WriteNumber("relayStatus", value[i]); writer.WriteEndObject(); } writer.WriteEndObject(); } }
Newtonsoft.Json(Json.NET) 版本
逻辑完全一致,仅适配库的API差异:
public class RelayArrayConverter : JsonConverter<int[]> { public override int[] ReadJson(JsonReader reader, Type objectType, int[] existingValue, bool hasExistingValue, JsonSerializer serializer) { var relayStatusArray = new int[6]; var relayRoot = JObject.Load(reader); foreach (var prop in relayRoot.Properties()) { if (!int.TryParse(prop.Name, out int relayIndex) || relayIndex <0 || relayIndex>5) continue; relayStatusArray[relayIndex] = prop.Value["relayStatus"]?.Value<int>() ?? 0; } return relayStatusArray; } public override void WriteJson(JsonWriter writer, int[] value, JsonSerializer serializer) { writer.WriteStartObject(); for (int i = 0; i < value.Length; i++) { writer.WritePropertyName(i.ToString()); writer.WriteStartObject(); writer.WritePropertyName("relayStatus"); writer.WriteValue(value[i]); writer.WriteEndObject(); } writer.WriteEndObject(); } public override bool CanWrite => true; }
调用方式
反序列化后直接按索引访问即可:
// System.Text.Json 调用 var device = JsonSerializer.Deserialize<DeviceData>(jsonText); int relay4Status = device.RelayStatus[4]; // 直接拿到示例里的1 // Newtonsoft.Json 调用 // var device = JsonConvert.DeserializeObject<DeviceData>(jsonText);
- 数组默认长度为6,缺失的继电器编号对应位置默认值为0,可根据业务需求调整默认值
- 内置非法键名过滤逻辑,遇到超出0-5范围的编号不会抛出异常
- 转换器同时支持序列化,将对象转回JSON时会自动还原成原结构,不会破坏接口约定的JSON格式
内容的提问来源于stack exchange,提问作者Mush
相关产品推荐
相关产品推荐

