【问题标题】:Keep casing when serializing dictionaries序列化字典时保留大小写
【发布时间】:2014-07-31 08:14:28
【问题描述】:

我有一个这样配置的 Web Api 项目:

config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

但是,我希望字典键的大小写保持不变。 Newtonsoft.Json 中是否有任何属性可以用于一个类来表示我希望在序列化过程中大小写保持不变?

public class SomeViewModel
{
    public Dictionary<string, string> Data { get; set; }    
}

【问题讨论】:

  • 你试过默认的解析器吗?
  • @Matthew 不,我没有;你能用一个例子解释一下代码的样子吗?请注意,我仍然希望对所有 Web api 请求进行骆驼案例序列化,我只希望对一个类(或者可能是任何字典键)进行自定义序列化。

标签: c# json.net


【解决方案1】:

没有属性可以做到这一点,但您可以通过自定义解析器来做到这一点。

我看到您已经在使用CamelCasePropertyNamesContractResolver。如果您从中派生一个新的解析器类并覆盖 CreateDictionaryContract() 方法,则可以提供一个不会更改键名的替代 DictionaryKeyResolver 函数。

这是您需要的代码:

class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
    {
        JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);

        contract.DictionaryKeyResolver = propertyName => propertyName;

        return contract;
    }
}

演示:

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo
        {
            AnIntegerProperty = 42,
            HTMLString = "<html></html>",
            Dictionary = new Dictionary<string, string>
            {
                { "WHIZbang", "1" },
                { "FOO", "2" },
                { "Bar", "3" },
            }
        };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(foo, settings);
        Console.WriteLine(json);
    }
}

class Foo
{
    public int AnIntegerProperty { get; set; }
    public string HTMLString { get; set; }
    public Dictionary<string, string> Dictionary { get; set; }
}

这是上面的输出。请注意,所有类属性名称都是驼峰式大小写,但字典键保留了它们原来的大小写。

{
  "anIntegerProperty": 42,
  "htmlString": "<html></html>",
  "dictionary": {
    "WHIZbang": "1",
    "FOO": "2",
    "Bar": "3"
  }
}

【讨论】:

  • 仅供参考,PropertyNameResolver 现已过时。看来contract.DictionaryKeyResolver = key =&gt; key; 工作正常。
  • 这仍然与匿名类型非常相关,尤其是当我们确实希望大多数结构使用驼峰式大小写,但不希望字典中的键被驼峰化时。
  • 完全同意克里斯的观点。我不得不在我的 JavaScript 中经历一些麻烦,只是因为我不能防止字典被驼峰式命名。事实证明,一行代码就可以解决这个问题(并使我的 JavaScript 变得更简单)!
  • @BrianRogers 效果很好!但是,您知道我是否可以仅在我的 Dictionary 属性具有一些自定义属性的情况下使用我的DictionaryKeyResolver 进行条件处理?
  • @Mugen 不是我的头顶。我建议将其作为一个新问题提出。如果您需要提供上下文,可以链接回此问题。
【解决方案2】:

Json.NET 9.0.1 引入了NamingStrategy 类层次结构来处理此类问题。它将属性名称的算法重新映射的逻辑从合约解析器提取到一个单独的轻量级类,该类允许控制是否重新映射 dictionary keysexplicitly specified property namesextension data names(在 10.0.1 中)。

通过使用DefaultContractResolver 并将NamingStrategy 设置为CamelCaseNamingStrategy 的实例,您可以通过在JsonSerializerSettings.ContractResolver 中设置它来生成带有驼峰式属性名称和未修改字典键的JSON:

var resolver = new DefaultContractResolver
{
    NamingStrategy = new CamelCaseNamingStrategy
    {
        ProcessDictionaryKeys = false,
        OverrideSpecifiedNames = true
    }
};
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = resolver;

注意事项:

  • CamelCasePropertyNamesContractResolver 的当前实现还指定具有明确指定属性名称的 .Net 成员(例如,已设置 JsonPropertyAttribute.PropertyName 的成员)应重新映射其名称:

    public CamelCasePropertyNamesContractResolver()
    {
        NamingStrategy = new CamelCaseNamingStrategy
        {
            ProcessDictionaryKeys = true,
            OverrideSpecifiedNames = true
        };
    }
    

    上面的resolver 保留了这种行为。如果你不想要这个,设置OverrideSpecifiedNames = false

  • Json.NET 有几个内置的命名策略,包括:

    1. CamelCaseNamingStrategy。骆驼案例命名策略,其中包含以前嵌入在 CamelCasePropertyNamesContractResolver 中的名称重新映射逻辑。
    2. SnakeCaseNamingStrategysnake case 命名策略。
    3. DefaultNamingStrategy。默认命名策略。属性名称和字典键不变。

    或者,您可以通过从抽象基类NamingStrategy 继承来创建自己的。

  • 虽然也可以修改CamelCasePropertyNamesContractResolver 实例的NamingStrategy,因为后者是shares contract information globally across all instances of each type,如果您的应用程序尝试使用多个@ 实例,这可能会导致意外的副作用987654358@。 DefaultContractResolver 不存在此类问题,因此在需要自定义大小写逻辑时使用它会更安全。

【讨论】:

  • 此解决方案不适用于public Dictionary&lt;string, Dictionary&lt;string, string&gt;&gt; Values { get; set; } 之类的属性。它仍然为内部字典键做驼峰式。
  • @hikalkan - 虽然我无法重现您的确切问题,但在使用 CamelCasePropertyNamesContractResolver 的多个实例时我能够找到问题。基本上NamingStrategy 第一个会影响第二个生成的合同。这可能就是你所看到的。请改用新建议,如果能解决您的问题,请告诉我。
  • 是否有灵活的NamingStrategy,使其能够同时解析camel case和pascal case?
  • @dbc 在初始代码示例中,config 应该是什么?
  • @RyanLundy - 我从最初的问题中复制了它,它显示了以下代码行:config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();。它看起来是 MVC 4 Web API HttpConfiguration,请参阅 How to set custom JsonSerializerSettings for Json.NET in MVC 4 Web API?
【解决方案3】:

这是一个非常好的答案。但是为什么不直接覆盖ResolveDictionaryKey

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

        protected override string ResolveDictionaryKey(string dictionaryKey)
        {
            return dictionaryKey;
        }

        #endregion
    }

【讨论】:

  • 非常简洁。谢谢分享。
【解决方案4】:

选择的答案是完美的,但我想当我输入这个时,合同解析器必须更改为这样的东西,因为 DictionaryKeyResolver 不再存在:)

public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
    {
        protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
        {
            JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
            contract.PropertyNameResolver = propertyName => propertyName;
            return contract;
        }
    }

【讨论】:

  • 其实反之亦然。您必须使用旧版本的 Json.Net。 DictionaryKeyResolver 被添加到 version 7.0.1 中,PropertyNameResolver 被标记为过时。
猜你喜欢
  • 2011-03-30
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
相关资源
最近更新 更多