【问题标题】:Deserialise Enum From JSON String with Spaces从带有空格的 JSON 字符串反序列化枚举
【发布时间】:2021-12-28 00:05:37
【问题描述】:

我会以我试图避免使用 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&lt;T&gt;() 完成,我希望能够通过此测试测试多种类型,因此我需要反射 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 由欧洲航天局控制,所以不知何故,我认为我无法说服他们进一步合理化响应。

有没有办法解决这个问题?


有些人要求提供我尝试反序列化的 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 不支持。请参阅:System.Text.Json: How do I specify a custom name for an enum value?,其中有一些建议的解决方法。
  • 反序列化RecordType 有效,因为正如docs 中所述,读取不区分大小写。 可以使用@ 自定义写入987654331@。请注意,这意味着 JsonNamingPolicy 无法自定义阅读。
  • Newtonsoft.Json 中工作正常。不要折磨自己。在System.Text.Json 中是可能的,但需要大量自定义代码或 Nuget。
  • 你能发布你拥有的真实json吗?很难理解是什么问题,为什么必须反序列化或序列化枚举?
  • @Serge - 已添加 :)

标签: c# json enums deserialization system.text.json


【解决方案1】:

这是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 即可。

如果你想使用转换器作为属性你必须添加一个无参数的构造函数到类JsonStringEnumConverterWithAttributeSupport

public JsonStringEnumConverterWithAttributeSupport() : this(namingPolicy : null, allowIntegerValues : true,
    parseEnumMemberAttribute : true, parseDisplayAttribute : false, parseDescriptionAttribute : false)
{

}

【讨论】:

  • 打算再等一个小时左右,看看是否有人知道一个像样的 System.Text.Json 选项,如果他们不知道,那么我会接受这个 :) 虽然它会如果您可以添加您所做工作的简短摘要,那很好,因为这些部分中没有对仅代码的答案进行评级(即使我可以看到您所做的事情)。干杯芽
  • @Persistence 我为System.Text.Json添加了一个版本
  • 谢谢你,我的好先生,你是一个学者和绅士。它需要[JsonConverter(typeof(JsonStringEnumConverterWithAttributeSupport))] 属性吗?
  • @Persistence 它可以使用我的示例中的选项开箱即用,如果要将转换器用作属性,则必须在 Library 类中添加无参数构造函数。 (我刚刚将 nuget 中的 5 个文件复制到我的项目中,所以我可以编辑它们)
  • 所以我刚刚在这里测试了这段代码,实际上我得到了与添加 nuget 之前相似的结果,我错过了什么吗?感谢您在这里提供帮助gist.github.com/hughesjs/2a34b68588fd2fae3935abdda2dea2e4
【解决方案2】:

试试这个,我使用 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
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    • 2013-09-04
    • 2013-09-01
    • 2019-02-28
    相关资源
    最近更新 更多