【问题标题】:Json.NET StringEnumConverter not always workingJson.NET StringEnumConverter 并不总是有效
【发布时间】:2015-03-27 01:14:33
【问题描述】:

我有一个枚举:

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; }
}

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

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

【问题讨论】:

  • 你试过用[JsonConverter(typeof(StringEnumConverter))]装饰FilterOperator本身吗?
  • 嗯,没想到可以这样设置。遗憾的是,它并没有解决问题,但也没有让问题变得更糟
  • 鉴于 'like' 和 'in' 的拼写与它们的装饰器相同,我怀疑你的装饰器实际上也没有为它们做任何事情。
  • 你是对的,我删除了 [JsonConverter(typeof(StringEnumConverter))] 并且它仍然工作相同。那么我是否滥用了 StringEnumConverter 和 EnumMember?
  • 这是这个问题的副本吗? stackoverflow.com/questions/8999731/…

标签: c# enums json.net


【解决方案1】:

好吧,如果您希望在类 GridFilter 的上下文之外反序列化时使用 enum 声明而不是 Operator 属性,则必须将 [JsonConverter(typeof(StringEnumConverter))] 属性直接放在 enum 声明上:

[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; }
}

【讨论】:

    【解决方案2】:

    感谢大家的帮助!我意识到我做了什么。可悲的是,这很愚蠢,我为跑来跑去提前道歉。

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

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

    【讨论】:

      【解决方案3】:

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

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

      要使用EnumMemberAttribute,请创建一个枚举并将DataContractAttribute 属性应用于枚举。然后将EnumMemberAttribute 属性应用于需要在序列化流中的每个成员。

      那当然是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
      }
      

      【讨论】:

      • 好主意,但没有任何区别。似乎 StringEnumConverter 被忽略了,因为它在有或没有它存在的情况下都可以工作
      • @Supergibbs 这也是我的猜测。我不知道你是否在底部看到了我的编辑,但源代码很清楚地说它应该可以工作。如果您手动添加 (How to tell Json.Net globally to apply the StringEnumConverter to all enums) 转换器,而不是依赖于该属性会怎样?我知道,不理想,但如果它很容易,让我们看看它是否有效,那么我们至少可以知道这一点。
      • 尝试了全局选项,没有运气
      • @Supergibbs 我不确定这是否让我感到惊讶,但无论哪种方式都很好知道。唔。我刚刚编辑了我的帖子,但基本上我试过了,它对我有用。所以肯定有其他事情发生。或者更确切地说,你能确认我得到的是你的预期行为吗?我假设是这样,但只是为了仔细检查,这就是应该发生的,对吧?
      • 好吧,我实际上遇到了反序列化问题。我做了和你一样的事情,它似乎在测试中有效。也许与我的项目设置有关,它变得非常庞大和复杂。我会尝试制作一个新的裸露的骨头来测试。感谢您的所有帮助!
      猜你喜欢
      • 1970-01-01
      • 2012-12-26
      • 1970-01-01
      • 1970-01-01
      • 2012-04-23
      • 2015-06-28
      • 2015-12-04
      • 2017-01-08
      • 1970-01-01
      相关资源
      最近更新 更多