从 JSON 带空格的字符串反序列化枚举
Deserialise Enum From JSON String with Spaces
我将在开头说明我试图避免使用 Newtonsoft.Json
,因为从表面上看,System.Text.Json
已准备好迎接 .NET 6 的黄金时段。
所以我有两个来自 API 的枚举,我想使用此测试方法反序列化它们:
[Theory]
[ClassData(typeof(TestDataGenerator))]
public void CanDeserialiseEnumsWithCustomJsonStrings(Enum expected, string jsonName)
{
jsonName.ShouldNotBeNullOrEmpty();
ReadOnlySpan<char> json = $"{{\"TestEnum\":\"{jsonName}\"}}";
Type constructed = typeof(TestEnumWrapper<>).MakeGenericType(expected.GetType());
var res = JsonSerializer.Deserialize(json, constructed);
constructed.GetProperty("TestEnum").GetValue(res).ShouldBe(expected);
}
private class TestEnumWrapper<T> where T: struct
{
public T TestEnum { get; set; }
}
(是的,我知道这可以通过 JsonSerializer.Deserialize<T>()
完成,我希望能够通过此测试测试多种类型,因此我需要反射 AFAICT)。
第一个,工作正常:
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum RecordType
{
[JsonPropertyName("country")]
Country = 1,
[JsonPropertyName("destinationOrbit")]
DestinationOrbit = 2,
[JsonPropertyName("engine")]
Engine = 3,
//etc...
}
第二个,反序列化失败,这似乎是由于名称中的空格。
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ObjectClass
{
[JsonPropertyName("Rocket Body")]
RocketBody,
[JsonPropertyName("Rocket Debris")]
RocketDebris,
[JsonPropertyName("Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[JsonPropertyName("Rocket Mission Related Object")]
RocketMissionRelatedObject,
//etc...
}
API 由欧洲 Space 机构控制,所以不知何故,我认为我无法说服他们使反应更加合理化。
有什么解决办法吗?
有些人要求提供 JSON 我正在尝试反序列化的示例。我目前正在处理此 blob 的属性部分:
{
"type": "object",
"attributes": {
"shape": null,
"xSectMin": null,
"satno": null,
"depth": null,
"objectClass": "Rocket Fragmentation Debris",
"cosparId": null,
"length": null,
"height": null,
"mass": null,
"xSectMax": null,
"vimpelId": 84303,
"xSectAvg": null,
"name": null
},
"relationships": {
"states": {
"links": {
"self": "/api/objects/61345/relationships/states",
"related": "/api/objects/61345/states"
}
},
"initialOrbits": {
"links": {
"self": "/api/objects/61345/relationships/initial-orbits",
"related": "/api/objects/61345/initial-orbits"
}
},
"destinationOrbits": {
"links": {
"self": "/api/objects/61345/relationships/destination-orbits",
"related": "/api/objects/61345/destination-orbits"
}
},
"operators": {
"links": {
"self": "/api/objects/61345/relationships/operators",
"related": "/api/objects/61345/operators"
}
},
"launch": {
"links": {
"self": "/api/objects/61345/relationships/launch",
"related": "/api/objects/61345/launch"
}
},
"reentry": {
"links": {
"self": "/api/objects/61345/relationships/reentry",
"related": "/api/objects/61345/reentry"
}
}
},
"id": "61345",
"links": {
"self": "/api/objects/61345"
}
}
这是 System.Text.Json
的解决方案
我使用了 Nuget 包 System.Text.Json.EnumExtensions
https://github.com/StefH/System.Text.Json.EnumExtensions
// you probably want these options somewhere global
private JsonSerializerOptions options;
private class TestEnumWrapper<T> where T : struct
{
public T TestEnum { get; set; }
}
public enum ObjectClass
{
[EnumMember(Value = "Rocket Body")]
RocketBody,
[EnumMember(Value = "Rocket Debris")]
RocketDebris,
[EnumMember(Value = "Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[EnumMember(Value = "Rocket Mission Related Object")]
RocketMissionRelatedObject,
//etc...
}
private void CanDeserialiseEnumsWithCustomJsonStrings(Enum expected, string jsonName)
{
var json = $"{{\"TestEnum\":\"{jsonName}\"}}";
Type constructed = typeof(TestEnumWrapper<>).MakeGenericType(expected.GetType());
var res = JsonSerializer.Deserialize(json, constructed, options);
}
public void Test()
{
this.options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverterWithAttributeSupport());
var testSerialize = JsonSerializer.Serialize(new TestEnumWrapper<ObjectClass>()
{ TestEnum = ObjectClass.RocketBody }, options);
// Test Deserialize
CanDeserialiseEnumsWithCustomJsonStrings(ObjectClass.RocketBody, "Rocket Body");
}
您只需将 new JsonStringEnumConverterWithAttributeSupport()
添加到 JsonSerializerOptions.Converters
即可。
如果要将转换器用作属性,则必须将无参数构造函数添加到 class JsonStringEnumConverterWithAttributeSupport
public JsonStringEnumConverterWithAttributeSupport() : this(namingPolicy : null, allowIntegerValues : true,
parseEnumMemberAttribute : true, parseDisplayAttribute : false, parseDescriptionAttribute : false)
{
}
试试这个,我使用 Newtonsoft.Json 并且为了测试我只反序列化了属性,因为只有它们包含枚举。您不需要任何自定义代码。
var attributes= JsonConvert.DeserializeObject<Root>(json);
类
public enum ObjectClass
{
[EnumMember(Value = "Rocket Body")]
RocketBody,
[EnumMember(Value ="Rocket Debris")]
RocketDebris,
[EnumMember(Value = "Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[EnumMember(Value ="Rocket Mission Related Object")]
RocketMissionRelatedObject
}
public partial class Root
{
[JsonProperty("attributes")]
public Attributes Attributes { get; set; }
}
public partial class Attributes
{
[JsonProperty("shape")]
public object Shape { get; set; }
[JsonProperty("xSectMin")]
public object XSectMin { get; set; }
[JsonProperty("satno")]
public object Satno { get; set; }
[JsonProperty("depth")]
public object Depth { get; set; }
[JsonProperty("objectClass")]
public ObjectClass ObjectClass { get; set; }
[JsonProperty("cosparId")]
public object CosparId { get; set; }
[JsonProperty("length")]
public object Length { get; set; }
[JsonProperty("height")]
public object Height { get; set; }
[JsonProperty("mass")]
public object Mass { get; set; }
[JsonProperty("xSectMax")]
public object XSectMax { get; set; }
[JsonProperty("vimpelId")]
public long VimpelId { get; set; }
[JsonProperty("xSectAvg")]
public object XSectAvg { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
测试
json=JsonConvert.SerializeObject(attributes);
attributes= JsonConvert.DeserializeObject<Root>(json);
结果
{
"attributes": {
"shape": null,
"xSectMin": null,
"satno": null,
"depth": null,
"objectClass": 2,
"cosparId": null,
"length": null,
"height": null,
"mass": null,
"xSectMax": null,
"vimpelId": 84303,
"xSectAvg": null,
"name": null
}
}
我将在开头说明我试图避免使用 Newtonsoft.Json
,因为从表面上看,System.Text.Json
已准备好迎接 .NET 6 的黄金时段。
所以我有两个来自 API 的枚举,我想使用此测试方法反序列化它们:
[Theory]
[ClassData(typeof(TestDataGenerator))]
public void CanDeserialiseEnumsWithCustomJsonStrings(Enum expected, string jsonName)
{
jsonName.ShouldNotBeNullOrEmpty();
ReadOnlySpan<char> json = $"{{\"TestEnum\":\"{jsonName}\"}}";
Type constructed = typeof(TestEnumWrapper<>).MakeGenericType(expected.GetType());
var res = JsonSerializer.Deserialize(json, constructed);
constructed.GetProperty("TestEnum").GetValue(res).ShouldBe(expected);
}
private class TestEnumWrapper<T> where T: struct
{
public T TestEnum { get; set; }
}
(是的,我知道这可以通过 JsonSerializer.Deserialize<T>()
完成,我希望能够通过此测试测试多种类型,因此我需要反射 AFAICT)。
第一个,工作正常:
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum RecordType
{
[JsonPropertyName("country")]
Country = 1,
[JsonPropertyName("destinationOrbit")]
DestinationOrbit = 2,
[JsonPropertyName("engine")]
Engine = 3,
//etc...
}
第二个,反序列化失败,这似乎是由于名称中的空格。
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ObjectClass
{
[JsonPropertyName("Rocket Body")]
RocketBody,
[JsonPropertyName("Rocket Debris")]
RocketDebris,
[JsonPropertyName("Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[JsonPropertyName("Rocket Mission Related Object")]
RocketMissionRelatedObject,
//etc...
}
API 由欧洲 Space 机构控制,所以不知何故,我认为我无法说服他们使反应更加合理化。
有什么解决办法吗?
有些人要求提供 JSON 我正在尝试反序列化的示例。我目前正在处理此 blob 的属性部分:
{
"type": "object",
"attributes": {
"shape": null,
"xSectMin": null,
"satno": null,
"depth": null,
"objectClass": "Rocket Fragmentation Debris",
"cosparId": null,
"length": null,
"height": null,
"mass": null,
"xSectMax": null,
"vimpelId": 84303,
"xSectAvg": null,
"name": null
},
"relationships": {
"states": {
"links": {
"self": "/api/objects/61345/relationships/states",
"related": "/api/objects/61345/states"
}
},
"initialOrbits": {
"links": {
"self": "/api/objects/61345/relationships/initial-orbits",
"related": "/api/objects/61345/initial-orbits"
}
},
"destinationOrbits": {
"links": {
"self": "/api/objects/61345/relationships/destination-orbits",
"related": "/api/objects/61345/destination-orbits"
}
},
"operators": {
"links": {
"self": "/api/objects/61345/relationships/operators",
"related": "/api/objects/61345/operators"
}
},
"launch": {
"links": {
"self": "/api/objects/61345/relationships/launch",
"related": "/api/objects/61345/launch"
}
},
"reentry": {
"links": {
"self": "/api/objects/61345/relationships/reentry",
"related": "/api/objects/61345/reentry"
}
}
},
"id": "61345",
"links": {
"self": "/api/objects/61345"
}
}
这是 System.Text.Json
我使用了 Nuget 包 System.Text.Json.EnumExtensions
https://github.com/StefH/System.Text.Json.EnumExtensions
// you probably want these options somewhere global
private JsonSerializerOptions options;
private class TestEnumWrapper<T> where T : struct
{
public T TestEnum { get; set; }
}
public enum ObjectClass
{
[EnumMember(Value = "Rocket Body")]
RocketBody,
[EnumMember(Value = "Rocket Debris")]
RocketDebris,
[EnumMember(Value = "Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[EnumMember(Value = "Rocket Mission Related Object")]
RocketMissionRelatedObject,
//etc...
}
private void CanDeserialiseEnumsWithCustomJsonStrings(Enum expected, string jsonName)
{
var json = $"{{\"TestEnum\":\"{jsonName}\"}}";
Type constructed = typeof(TestEnumWrapper<>).MakeGenericType(expected.GetType());
var res = JsonSerializer.Deserialize(json, constructed, options);
}
public void Test()
{
this.options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverterWithAttributeSupport());
var testSerialize = JsonSerializer.Serialize(new TestEnumWrapper<ObjectClass>()
{ TestEnum = ObjectClass.RocketBody }, options);
// Test Deserialize
CanDeserialiseEnumsWithCustomJsonStrings(ObjectClass.RocketBody, "Rocket Body");
}
您只需将 new JsonStringEnumConverterWithAttributeSupport()
添加到 JsonSerializerOptions.Converters
即可。
如果要将转换器用作属性,则必须将无参数构造函数添加到 class JsonStringEnumConverterWithAttributeSupport
public JsonStringEnumConverterWithAttributeSupport() : this(namingPolicy : null, allowIntegerValues : true,
parseEnumMemberAttribute : true, parseDisplayAttribute : false, parseDescriptionAttribute : false)
{
}
试试这个,我使用 Newtonsoft.Json 并且为了测试我只反序列化了属性,因为只有它们包含枚举。您不需要任何自定义代码。
var attributes= JsonConvert.DeserializeObject<Root>(json);
类
public enum ObjectClass
{
[EnumMember(Value = "Rocket Body")]
RocketBody,
[EnumMember(Value ="Rocket Debris")]
RocketDebris,
[EnumMember(Value = "Rocket Fragmentation Debris")]
RocketFragmentationDebris,
[EnumMember(Value ="Rocket Mission Related Object")]
RocketMissionRelatedObject
}
public partial class Root
{
[JsonProperty("attributes")]
public Attributes Attributes { get; set; }
}
public partial class Attributes
{
[JsonProperty("shape")]
public object Shape { get; set; }
[JsonProperty("xSectMin")]
public object XSectMin { get; set; }
[JsonProperty("satno")]
public object Satno { get; set; }
[JsonProperty("depth")]
public object Depth { get; set; }
[JsonProperty("objectClass")]
public ObjectClass ObjectClass { get; set; }
[JsonProperty("cosparId")]
public object CosparId { get; set; }
[JsonProperty("length")]
public object Length { get; set; }
[JsonProperty("height")]
public object Height { get; set; }
[JsonProperty("mass")]
public object Mass { get; set; }
[JsonProperty("xSectMax")]
public object XSectMax { get; set; }
[JsonProperty("vimpelId")]
public long VimpelId { get; set; }
[JsonProperty("xSectAvg")]
public object XSectAvg { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
测试
json=JsonConvert.SerializeObject(attributes);
attributes= JsonConvert.DeserializeObject<Root>(json);
结果
{
"attributes": {
"shape": null,
"xSectMin": null,
"satno": null,
"depth": null,
"objectClass": 2,
"cosparId": null,
"length": null,
"height": null,
"mass": null,
"xSectMax": null,
"vimpelId": 84303,
"xSectAvg": null,
"name": null
}
}