如何用Json.NET将十六进制键的JSON字典反序列化为Dictionary<uint, T>
如何将JSON中十六进制键的字典反序列化为Dictionary<uint, T>
问题场景
我有一个包含特殊字典结构的复杂JSON对象,示例结构如下:
{ ... "SomeDictionary": { "0x010": { "a": "b" }, "0x020": { "a": "b" }, "0x030": { "a": "b" } } ... }
需要将其中键为十六进制字符串(格式为0xXXX)的字典反序列化为.NET中的Dictionary<uint, T>类型(T为字典值的具体类型)。
已排除的方案
- 为每个目标字典单独创建转换器:存在多个此类字典,不想重复定义转换器(即使泛型实现也不愿采用)
- 使用类型转换器:不想将字典键包装到自定义类中
当前采用的可行方案
通过DTO中转实现转换:
- 定义包含
Dictionary<string, MyType>的DTO类:
public record class ComplexObjectDTO( // 其他属性 Dictionary<string, MyType> SomeDictionary // 其他属性 );
- 反序列化到DTO后,通过辅助函数转换为领域对象的
Dictionary<uint, T>:
private static Dictionary<uint, T> ToUintDictionary<T>(Dictionary<string, T> source) => source.ToDictionary( x => { var str = x.Key; if (!str.StartsWith("0x")) throw new Exception($"Expected hexadecimal number, got {str} instead"); return Convert.ToUInt32(str, 16); }, x => x.Value );
该方案因本身需要使用DTO而可行,但希望找到更直接的替代方案。
替代解决方案
方案1:全局契约解析器(推荐)
自定义契约解析器,自动处理所有Dictionary<uint, T>类型的键序列化/反序列化,无需为每个字典单独配置:
public class UIntKeyContractResolver : DefaultContractResolver { protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { var contract = base.CreateDictionaryContract(objectType); // 仅处理键类型为uint的字典 if (objectType.GetGenericArguments()[0] == typeof(uint)) { // 序列化时将uint键转为0x开头的十六进制字符串 contract.DictionaryKeyResolver = key => { if (key is uint uintKey) return $"0x{uintKey:X}"; return key.ToString(); }; // 反序列化时将0x开头的字符串键转为uint contract.DictionaryKeyDeserializer = (reader, keyType) => { if (reader.TokenType != JsonToken.PropertyName) throw new JsonSerializationException("Expected property name"); var keyStr = reader.Value.ToString(); if (!keyStr.StartsWith("0x")) throw new JsonSerializationException($"Expected hexadecimal key, got {keyStr} instead"); if (!uint.TryParse(keyStr.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var uintKey)) throw new JsonSerializationException($"Invalid hexadecimal key: {keyStr}"); return uintKey; }; } return contract; } }
使用方式:
在反序列化时指定契约解析器,所有Dictionary<uint, T>属性都会自动适配:
var settings = new JsonSerializerSettings { ContractResolver = new UIntKeyContractResolver() }; var domainObject = JsonConvert.DeserializeObject<ComplexDomainObject>(jsonString, settings);
方案2:通用泛型字典转换器
创建针对Dictionary<uint, T>的泛型转换器,可全局注册或按需标记属性:
public class UIntKeyDictionaryConverter<T> : JsonConverter<Dictionary<uint, T>> { public override Dictionary<uint, T> ReadJson(JsonReader reader, Type objectType, Dictionary<uint, T> existingValue, bool hasExistingValue, JsonSerializer serializer) { var result = new Dictionary<uint, T>(); if (reader.TokenType != JsonToken.StartObject) return result; while (reader.Read()) { if (reader.TokenType == JsonToken.EndObject) break; if (reader.TokenType != JsonToken.PropertyName) continue; var keyStr = reader.Value.ToString(); if (!keyStr.StartsWith("0x")) throw new JsonSerializationException($"Expected hexadecimal key, got {keyStr} instead"); if (!uint.TryParse(keyStr.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var key)) throw new JsonSerializationException($"Invalid hexadecimal key: {keyStr}"); reader.Read(); var value = serializer.Deserialize<T>(reader); result.Add(key, value); } return result; } public override void WriteJson(JsonWriter writer, Dictionary<uint, T> value, JsonSerializer serializer) { writer.WriteStartObject(); foreach (var kvp in value) { writer.WritePropertyName($"0x{kvp.Key:X}"); serializer.Serialize(writer, kvp.Value); } writer.WriteEndObject(); } }
使用方式:
- 全局注册(针对特定T类型):
var settings = new JsonSerializerSettings(); settings.Converters.Add(new UIntKeyDictionaryConverter<MyType>()); // 若有多个T类型,可添加多个对应转换器实例
- 按需标记属性:
public class ComplexDomainObject { [JsonConverter(typeof(UIntKeyDictionaryConverter<MyType>))] public Dictionary<uint, MyType> SomeDictionary { get; set; } }
内容的提问来源于stack exchange,提问作者MrAkroMenToS
相关产品推荐
相关产品推荐

