C# WPF自定义JSON转换器:对象数组转数组数组适配Python服务端
我有一个C# WPF应用程序,需要向独立运行的Python服务端发送JSON请求。目前已经通过ListBox、ObservableCollection和JsonSerializer实现了部分成功通信,代码如下:
try { // PeekSignalsList 是 ObservableCollection StringContent? jsonString = new StringContent( JsonConvert.SerializeObject(PeekSignalsList, Formatting.Indented), Encoding.UTF8, new MediaTypeHeaderValue("application/json")); if (jsonString is not null) { Client.DefaultRequestHeaders.Clear(); Client.DefaultRequestHeaders.Add("host", $"{ServerAddress}"); Client.DefaultRequestHeaders.Add("user-agent", "python-requests/2.31.0"); Client.DefaultRequestHeaders.Add("accept-encoding", "gzip, deflate"); Client.DefaultRequestHeaders.Add("accept", "*/*"); Client.DefaultRequestHeaders.Add("connection", "keep-alive"); Client.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json"); // 这段代码可以正常工作 var request = new System.Net.Http.HttpRequestMessage { Method = System.Net.Http.HttpMethod.Get, RequestUri = new Uri($"http://{ServerAddress}/peek_data"), Content = jsonString, }; var response = await Client.SendAsync(request).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); } else { PeekLog += $"ECI-PeekOneShot: Json serialization returned null{Environment.NewLine}"; } }
格式不匹配导致请求被拒
我还有一个ListView控件,其两个字段绑定到另一个PokeSignal类型的ObservableCollection。使用上述相同方法发送请求时,Wireshark捕获到的JSON格式与Python测试脚本发送的格式存在差异,导致服务端拒绝请求。
Python测试脚本发送的JSON格式(Wireshark捕获)
JSON raw form: [ [ "counter", 11 ] ]
对应的结构解析:
Array
Array
[Path with value: /[]/[]:counter]
[Member with value: []:counter]
String value: counter
[Path with value: /[]/[]:11]
[Member with value: []:11]
Number value: 11
C#程序发送的JSON格式(Wireshark捕获)
JSON raw form: [ { "SignalName": "counter", "SignalValue": 0 } ]
对应的结构解析:
Array
Object
Member: SignalName
[Path with value: /[]/SignalName:counter]
[Member with value: SignalName:counter]
String value: counter
Key: SignalName
[Path: /[]/SignalName]
Member: SignalValue
[Path with value: /[]/SignalValue:0]
[Member with value: SignalValue:0]
Number value: 0
Key: SignalValue
[Path: /[]/SignalValue]
自定义序列化转换器的尝试及错误
我知道需要自定义JSON序列化器来生成数组的数组格式,但尝试后遇到问题。
PokeSignal类代码
namespace EngineeringLabUi.Models { [JsonConverter(typeof(PokeSignalJsonConverter))] public class PokeSignal { public PokeSignal(string? signalName, int? signalValue) { SignalName = signalName; SignalValue = signalValue; } public PokeSignal() { SignalName = string.Empty; SignalValue = 0; } // 不确定这里该怎么实现 public override string ToString() => $"{SignalName}:{SignalValue}"; public static PokeSignal Parse(string input) { // 不确定这里该怎么实现 int value = int.Parse(input.Substring(0, input.Length - 1)); string name = input; return new PokeSignal(name, value); } public string? SignalName { get; set; } public int? SignalValue { get; set; } } }
自定义转换器代码
namespace EngineeringLabUi.Converters { public class PokeSignalJsonConverter : JsonConverter<PokeSignal> { public override PokeSignal? ReadJson(JsonReader reader, Type objectType, PokeSignal? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer) { return PokeSignal.Parse(reader.ToString()!); } public override void WriteJson(JsonWriter writer, PokeSignal? value, Newtonsoft.Json.JsonSerializer serializer) { writer.WriteRaw(value?.ToString()); } } }
使用转换器时的错误
尝试使用转换器时出现编译错误:
try { var serializeOptions = new JsonSerializerOptions { WriteIndented = true, Converters = { /* 错误: 无法将类型 'EngineeringLabUi.Converters.PokeSignalJsonConverter' 转换为 * 'System.Text.Json.Serialization.JsonConverter' * * 错误: 集合初始化器的最佳重载 Add 方法 'ICollection<JsonConverter>.Add(JsonConverter)' * 具有一些无效参数 */ new PokeSignalJsonConverter() } }; StringContent? jsonString = new StringContent( JsonConvert.SerializeObject(PokeSignalsList, Formatting.Indented), Encoding.UTF8, new MediaTypeHeaderValue("application/json")); if (jsonString is not null) { Client.DefaultRequestHeaders.Clear(); Client.DefaultRequestHeaders.Add("host", $"{ServerAddress}"); Client.DefaultRequestHeaders.Add("user-agent", "python-requests/2.31.0"); Client.DefaultRequestHeaders.Add("accept-encoding", "gzip, deflate"); Client.DefaultRequestHeaders.Add("accept", "*/*"); Client.DefaultRequestHeaders.Add("connection", "keep-alive"); Client.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json"); var request = new System.Net.Http.HttpRequestMessage { Method = System.Net.Http.HttpMethod.Get, RequestUri = new Uri($"http://{ServerAddress}/poke_data"), Content = jsonString, }; var response = await Client.SendAsync(request).ConfigureAwait(false); response.EnsureSuccessStatusCode(); } else { PokeLog += $"ECI-PokeOneShot: Json serialization returned null{Environment.NewLine}"; } }
最终解决方案
核心问题在于混淆了不同JSON序列化包(Newtonsoft.Json和System.Text.Json)的转换器,以及对序列化逻辑的误解。更新后的PokeSignal代码如下:
[Newtonsoft.Json.JsonConverter(typeof(ObjectToArrayConverter<PokeSignal>))] [DataContract] public class PokeSignal(string? signalName, int? signalValue) { public PokeSignal() : this(string.Empty, 0) { } [JsonProperty(Order = 1)] public string? SignalName { get; set; } = signalName; [JsonProperty(Order = 12)] public int? SignalValue { get; set; } = signalValue; }
学习要点
- 明确目标JSON格式的结构要求
- 区分使用的JSON序列化包(Newtonsoft.Json vs System.Text.Json),两者的转换器不兼容
- 自定义转换器需正确重写读写方法,对应目标格式的解析与生成逻辑
- 通过特性标记指定要使用的转换器,确保序列化时正确调用
- 调用序列化方法时需匹配对应的包(如使用
JsonConvert.SerializeObject对应Newtonsoft.Json)
内容的提问来源于stack exchange,提问作者Michael Riley

