辞書をシリアル化するときは大文字と小文字を区別してください


92

次のように構成されているWebApiプロジェクトがあります。

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

ただし、辞書キーのケーシングは変更しないでください。Newtonsoft.Jsonシリアル化中にケーシングを変更しないことを示すためにクラスに使用できる属性はありますか?

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

1
デフォルトのリゾルバーを試しましたか?
マシュー

1
@Matthewいいえ、していません。コードがどのように見えるかを例で説明できますか?すべてのWebAPIリクエストにキャメルケースのシリアル化が必要なことに注意してください。クラス(または辞書キー)のカスタムシリアル化が必要なだけです。
zafeiris.m 2014年

回答:


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
これは、特に構造の大部分にキャメルケースが必要であるが、辞書内のキーがキャメルケース化されていない場合は特に、匿名タイプに非常に関連しています。
Chris Schaller 2015

クリスに絶対に同意します。辞書がキャメルケースに入れられるのを防ぐことができないという理由だけで、JavaScriptでフープを実行することを余儀なくされました。1行のコードでこの問題が解決する(そしてJavaScriptがはるかに簡単になる)ことがわかりました!
Stephen Chung

@BrianRogersはうまく機能します!ただし、DictionaryKeyResolverDictionaryプロパティにカスタム属性がある場合にのみ、自分を使用して条件付けできるかどうかを知っていますか?
ムゲン

@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; }。それでも、内部辞書キーに対してキャメルケースを実行します。
hikalkan 2017年

@ hikalkan-正確な問題を再現することはできませんでしたが、複数のインスタンスを使用すると問題を見つけることができました。 CamelCasePropertyNamesContractResolver。基本的NamingStrategyに最初のものは、2番目によって生成された契約に影響を与えます。それあなたが見ているものかもしれません。代わりに新しい推奨事項を試して、問題が解決するかどうかをお知らせください。
dbc 2017年

1
NamingStrategyキャメルケースとパスカルケースの両方を解析できるように、柔軟性はありますか?
シミーワイツハンドラー2017

@dbc最初のコードサンプルでは、​​何がconfig想定されていますか?
ライアンランディ

@ RyanLundy-最初の質問からコピーしましたconfig.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();。次のコード行が表示されています。MVC 4 WebAPIのようですHttpConfiguration WebAPIのようです。MVC4WebAPIでJson.NETのカスタムJsonSerializerSettingsを設定する方法を参照してください
dbc 2018

12

それはとてもいい答えです。しかし、なぜ単にオーバーライドしないのResolveDictionaryKeyですか?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }

非常に簡潔です。共有するためのThx。
アブアブドラ2016年

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廃止とマークされました。
ブライアンロジャース
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.