Json.NET StringEnumConverter 并不总是有效

Json.NET StringEnumConverter not always working

我有一个枚举:

public enum FilterOperator
    {
        [EnumMember(Value = "eq")]
        Equals,
        [EnumMember(Value = "gt")]
        GreaterThan,
        [EnumMember(Value = "lt")]
        LessThan,
        [EnumMember(Value = "in")]
        In,
        [EnumMember(Value = "like")]
        Like
    }

和一个包含枚举 属性 的 class:

public class GridFilter
{
    [JsonProperty("operator")]
    [JsonConverter(typeof(StringEnumConverter))]
    public FilterOperator Operator { get; set; }
}

该对象通过 WebAPI 操作传入,并按预期对 "like" 和 "in" 进行反序列化,但对 "lg" 或 "gt" 则不然。知道为什么吗?

更新:"like" 和 "in" 工作的原因是它们匹配枚举名称。将 GreaterThan 重命名为 Gt(等)有效。所以真正的问题是为什么不使用 StringEnumConverter?

这只是猜测,我没有测试过。

我查看了 EnumMemberAttribute 的文档,上面写着:

To use EnumMemberAttribute, create an enumeration and apply the DataContractAttribute attribute to the enumeration. Then apply the EnumMemberAttribute attribute to each member that needs to be in the serialization stream.

当然是 DataContractSerializer,但我想也许 JSON.net 会考虑同样的规则?

我会尝试将 [DataContract] 应用于 enum

[DataContract]
public enum FilterOperator
{
    [EnumMember(Value = "eq")]
    Equals,
    [EnumMember(Value = "gt")]
    GreaterThan,
    [EnumMember(Value = "lt")]
    LessThan,
    [EnumMember(Value = "in")]
    In,
    [EnumMember(Value = "like")]
    Like
}

这似乎是随意的,而且是多余的。我知道 JSON.net 通常 不依赖于这类事情,但也许在这种情况下它依赖?

我还注意到,如果存在 [DataContract]DataContractSerializer 似乎会忽略不带 [EnumMember] 的元素,因此为了向后兼容,可能必须采用这种方式。同样,不是超级合乎逻辑。但我只有这些了。


编辑: 以真正的开发人员方式,我不只是测试,而是进入了源代码。读取 EnumMemberAttribute 的部分可以在第 55 行的 here 中找到,它是这样做的:

n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true) 
      .Cast<EnumMemberAttribute>() 
      .Select(a => a.Value) 
      .SingleOrDefault() ?? f.Name; 

这让我觉得你所拥有的应该可以工作。


编辑 2:

好吧,这很奇怪。我自己试了一下,发现它有效。

public enum FilterOperator
{
    [EnumMember(Value = "eq")]
    Equals,
    [EnumMember(Value = "gt")]
    GreaterThan,
    [EnumMember(Value = "lt")]
    LessThan,
    [EnumMember(Value = "in")]
    In,
    [EnumMember(Value = "like")]
    Like
}
public class GridFilter
{
    [JsonProperty("operator")]
    [JsonConverter(typeof(StringEnumConverter))]
    public FilterOperator Operator { get; set; }
}


[TestMethod]
public void enumTest()
{
    GridFilter gf = new GridFilter()
    {
        Operator = FilterOperator.GreaterThan
    };

    var json = JsonConvert.SerializeObject(gf);

    // json yields {"operator":"gt"}

    var ret = JsonConvert.DeserializeObject<GridFilter>(json);
    // ret.Operator yields FilterOperator.GreaterThan
}

感谢大家的帮助!我意识到我做了什么。可悲的是,它非常愚蠢,我提前为 运行 周围的事情道歉。

因为我使用的是 GET,所以我将参数作为 url 查询参数发送,因此 WebAPI 使用普通的 ModelBinder 来映射名称,而不是 JSON.NET。我实际上并没有发送 JSON,所以这完全有道理。这个问题帮助我意识到这一点: Complex type is getting null in a ApiController parameter

我的选择是创建一个自定义模型绑定器来正确处理枚举,或者更改为 POST 并使用 JSON.stringify() 发送数据。

好吧,如果你想要的话,你必须将 [JsonConverter(typeof(StringEnumConverter))] 属性直接放在 enum 声明上,而不是 GridFilterOperator 属性在 class GridFilter:

的上下文之外反序列化时使用
[JsonConverter(typeof(StringEnumConverter))] // Add this
public enum FilterOperator
{
    [EnumMember(Value = "eq")]
    Equals,
    [EnumMember(Value = "gt")]
    GreaterThan,
    [EnumMember(Value = "lt")]
    LessThan,
    [EnumMember(Value = "in")]
    In,
    [EnumMember(Value = "like")]
    Like
}

public class GridFilter
{
    [JsonProperty("operator")]
    //[JsonConverter(typeof(StringEnumConverter")] // Remove this
    public FilterOperator Operator { get; set; }
}