序列化词典时保留外壳

时间:2014-06-10 14:07:37

标签: c# json.net

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

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

但是,我希望字典键的外壳保持不变。 Newtonsoft.Json中是否有任何属性我可以用来表示我希望在序列化期间保持套管不变?

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

4 个答案:

答案 0 :(得分:115)

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

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

答案 1 :(得分:55)

Json.NET 9.0.1引入了NamingStrategy类层次结构来处理这类问题。它提取了将合同解析器中的属性名称算法重新映射到一个单独的轻量级类的逻辑,该类允许控制dictionary keysexplicitly specified property namesextension data names10.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. 中的名称重新映射逻辑
    3. SnakeCaseNamingStrategysnake case命名策略。
    4. DefaultNamingStrategy。默认命名策略。属性名称和字典键保持不变。
    5. 或者,您可以通过继承抽象基类NamingStrategy来创建自己的。

    6. 虽然也可以修改CamelCasePropertyNamesContractResolver实例的NamingStrategy,但由于后者为shares contract information globally across all instances of each type,如果您的应用尝试,这可能会导致意外的副作用使用CamelCasePropertyNamesContractResolver的多个实例。 DefaultContractResolver不存在这样的问题,因此在需要对套管逻辑进行任何自定义时使用它会更安全。

答案 2 :(得分:11)

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

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }

答案 3 :(得分:1)

选择的答案是完美的,但我想在我打字时,合同解析器必须更改为类似的东西,因为DictionaryKeyResolver不再存在:)

fftfilt