序列化字典时保留大小写


92

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

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

但是,我希望字典键大小写保持不变。Newtonsoft.Json我可以在类中使用任何属性来表示我希望在序列化期间大小写保持不变吗?

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

1
您是否尝试过默认解析器?
马修

1
@马修不,我没有;您可以举一个例子说明代码的样子吗?注意,我仍然希望所有Web api请求都使用Camel大小写序列化,我只想为一个类(或可能对任何字典键)进行自定义序列化。
zafeiris.m 2014年

Answers:


132

没有属性可以执行此操作,但是您可以通过自定义解析程序来执行此操作。

我发现您已经在使用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"
  }
}

2
仅供参考,PropertyNameResolver现在已过时。看来contract.DictionaryKeyResolver = key => key;效果很好。
John Gietzen 2015年

1
这仍然与匿名类型非常相关,尤其是当我们确实希望为大多数结构使用驼峰式大小写,但又不想对字典中的键进行驼峰处理时。
克里斯·谢勒

完全同意克里斯。仅仅因为我无法阻止字典中的驼峰驼峰,我就不得不在JavaScript中穿梭。事实证明,只有一行代码可以解决此问题(并使我的JavaScript简单得多)!
斯蒂芬·钟

@BrianRogers很棒!但是,您知道DictionaryKeyResolver我的字典属性具有某些自定义属性时,是否可以使用我的条件?
Mugen

@Mugen不在我的头顶上。我建议将其作为一个新问题提出。如果需要提供上下文,则可以链接回此问题。
布赖恩·罗杰斯

67

Json.NET 9.0.1引入了NamingStrategy类层次结构来处理此类问题。它从合同解析器提取属性名称的算法重映射逻辑到一个单独的轻量级类,该类允许控制是否重映射字典键显式指定的属性名称扩展数据名称(在10.0.1中)。

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

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

笔记:

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

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

    上面resolver保留了这种行为。如果您不想这样做,请设置OverrideSpecifiedNames = false

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

    1. CamelCaseNamingStrategy。骆驼式的命名策略,其中包含以前嵌入的名称重映射逻辑CamelCasePropertyNamesContractResolver
    2. SnakeCaseNamingStrategy。一个蛇的情况下的命名策略。
    3. DefaultNamingStrategy。默认的命名策略。属性名称和字典键保持不变。

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

  • 尽管也可以修改的NamingStrategy一个实例的CamelCasePropertyNamesContractResolver,但由于后者在每种类型的所有实例之间全局共享合同信息,因此,如果您的应用程序尝试使用的多个实例,则可能会导致意外的副作用CamelCasePropertyNamesContractResolver。,没有这样的问题DefaultContractResolver,因此,在需要对大小写逻辑进行任何自定义时,使用起来更安全。


此解决方案不适用于的属性public Dictionary<string, Dictionary<string, string>> Values { get; set; }。它仍然对内部字典键使用camelCase。
hikalkan

@hikalkan-虽然我无法重现您的确切问题,但是在使用的多个实例时,我能够找到问题CamelCasePropertyNamesContractResolver。基本上NamingStrategy,第一个会影响第二个生成的合同。那可能就是您所看到的。请尝试新的建议,并让我知道是否可以解决您的问题。
dbc

1
是否有一个灵活的NamingStrategy,以便它能够解析驼峰和帕斯卡大小写?
Shimmy Weitzhandler,

@dbc在初始代码示例中,config应该是什么?
瑞安·伦迪

@RyanLundy-我从最初的问题中复制了它,它显示了以下代码行:config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();。它看起来像是MVC 4 Web API HttpConfiguration,请参阅如何在MVC 4 Web API中为Json.NET设置自定义JsonSerializerSettings?
dbc

12

这是一个很好的答案。但是,为什么不改写ResolveDictionaryKey呢?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }

非常简洁。谢谢分享。
阿布阿卜杜拉

1

选定的答案是完美的,但是我想在我键入此答案时,合同解析器必须更改为类似的内容,因为DictionaryKeyResolver不再存在:)

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

4
实际上,事实恰恰相反。您必须使用旧版本的Json.Net。 DictionaryKeyResolver已在7.0.1版中添加,并PropertyNameResolver标记为过时。
布赖恩·罗杰斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.