Jaga casing saat membuat serial kamus

93

Saya memiliki proyek Web Api yang dikonfigurasi seperti ini:

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

Namun, saya ingin casing kunci kamus tetap tidak berubah. apakah ada atribut di dalam yang Newtonsoft.Jsondapat saya gunakan ke kelas untuk menunjukkan bahwa saya ingin casing tetap tidak berubah selama serialisasi?

public class SomeViewModel
{
    public Dictionary<string, string> Data { get; set; }    
}
zafeiris.m
sumber
1
Sudahkah Anda mencoba resolver default?
Matius
1
@ Tidak, saya belum; dapatkah Anda menjelaskan dengan contoh bagaimana kode itu akan terlihat? Catatan, saya masih ingin serialisasi kasus Camel untuk semua permintaan api web saya, saya hanya ingin serialisasi khusus untuk kelas (atau mungkin untuk kunci kamus apa pun).
zafeiris.m

Jawaban:

134

Tidak ada atribut untuk melakukan ini, tetapi Anda dapat melakukannya dengan menyesuaikan resolver.

Saya melihat bahwa Anda sudah menggunakan file CamelCasePropertyNamesContractResolver. Jika Anda mendapatkan kelas resolver baru dari itu dan mengganti CreateDictionaryContract()metode, Anda dapat menyediakan DictionaryKeyResolverfungsi pengganti yang tidak mengubah nama kunci.

Berikut kode yang Anda perlukan:

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

        contract.DictionaryKeyResolver = propertyName => propertyName;

        return contract;
    }
}

Demo:

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

Ini adalah keluaran dari atas. Perhatikan bahwa semua nama properti kelas menggunakan camel-cased, tetapi kunci kamus tetap menggunakan casing aslinya.

{
  "anIntegerProperty": 42,
  "htmlString": "<html></html>",
  "dictionary": {
    "WHIZbang": "1",
    "FOO": "2",
    "Bar": "3"
  }
}
Brian Rogers
sumber
2
FYI, PropertyNameResolver sekarang sudah usang. Sepertinya itu contract.DictionaryKeyResolver = key => key;berfungsi dengan baik.
John Gietzen
1
Ini masih SANGAT relevan dengan tipe anonim, terutama ketika kita memang menginginkan casing unta untuk sebagian besar struktur, tetapi tidak ingin kunci di dalam kamus menjadi camelised.
Chris Schaller
Sangat setuju dengan Chris. Saya telah dipaksa untuk melewati rintangan di JavaScript saya hanya karena saya tidak bisa mencegah kamus dari camelCased. Ternyata satu baris kode akan menyelesaikan masalah ini (dan membuat JavaScript saya jauh lebih sederhana)!
Stephen Chung
@BrianRogers Bekerja dengan baik! Namun, tahukah Anda jika saya dapat mengondisikan menggunakan DictionaryKeyResolverhanya saya jika properti Dictionary saya memiliki beberapa Atribut kustom?
Mugen
@Mugen Tidak lepas dari kepalaku. Saya akan merekomendasikan menanyakan itu sebagai Pertanyaan baru. Anda dapat menautkan kembali ke pertanyaan ini jika Anda perlu memberikan konteks.
Brian Rogers
67

Json.NET 9.0.1 memperkenalkan NamingStrategyhierarki kelas untuk menangani masalah semacam ini. Ini mengekstrak logika untuk pemetaan ulang algoritmik nama properti dari resolver kontrak ke kelas ringan yang terpisah yang memungkinkan untuk mengontrol apakah kunci kamus , nama properti yang ditentukan secara eksplisit , dan nama data ekstensi (dalam 10.0.1 ) dipetakan ulang.

Dengan menggunakan DefaultContractResolverdan menyetel NamingStrategyke sebuah instance, CamelCaseNamingStrategyAnda dapat membuat JSON dengan nama properti berbasis unta dan kunci kamus yang tidak dimodifikasi dengan menyetelnya di JsonSerializerSettings.ContractResolver:

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

Catatan:

  • Implementasi saat ini CamelCasePropertyNamesContractResolverjuga menetapkan bahwa anggota .Net dengan nama properti yang ditentukan secara eksplisit (misalnya yang JsonPropertyAttribute.PropertyNametelah ditetapkan) harus memiliki nama yang dipetakan ulang:

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

    Hal di atas resolvermempertahankan perilaku ini. Jika Anda tidak menginginkan ini, setel OverrideSpecifiedNames = false.

  • Json.NET memiliki beberapa strategi penamaan bawaan termasuk:

    1. CamelCaseNamingStrategy. Strategi penamaan kasus unta yang berisi logika pemetaan ulang nama yang sebelumnya tertanam di CamelCasePropertyNamesContractResolver.
    2. SnakeCaseNamingStrategy. Sebuah kasus ular strategi penamaan.
    3. DefaultNamingStrategy. Strategi penamaan default. Nama properti dan kunci kamus tidak berubah.

    Atau, Anda dapat membuatnya sendiri dengan mewarisi dari kelas dasar abstrak NamingStrategy.

  • Meskipun dimungkinkan juga untuk mengubah NamingStrategysebuah instance CamelCasePropertyNamesContractResolver, karena yang terakhir membagikan informasi kontrak secara global di semua instance dari setiap jenis , hal ini dapat menyebabkan efek samping yang tidak terduga jika aplikasi Anda mencoba menggunakan beberapa instance CamelCasePropertyNamesContractResolver. Tidak ada masalah seperti itu DefaultContractResolver, jadi lebih aman digunakan saat kustomisasi logika casing diperlukan.

dbc
sumber
Solusi ini tidak berfungsi untuk properti seperti public Dictionary<string, Dictionary<string, string>> Values { get; set; }. Itu masih camelCase untuk kunci kamus bagian dalam.
hikalkan
@ Hikalkan - meskipun saya tidak dapat mereproduksi masalah Anda yang sebenarnya, saya dapat menemukan masalah saat menggunakan beberapa contoh CamelCasePropertyNamesContractResolver. Pada dasarnya NamingStrategyuntuk yang pertama akan mempengaruhi kontrak yang dihasilkan oleh yang kedua. Itu mungkin yang Anda lihat. Coba rekomendasi baru sebagai gantinya dan beri tahu saya jika ini dapat memperbaiki masalah Anda.
dbc
1
Apakah ada yang fleksibel NamingStrategy, sehingga mampu mengurai casing unta dan casing pascal?
Shimmy Weitzhandler
@dbc Dalam contoh kode awal, apa yang configseharusnya?
Ryan Lundy
@RyanLundy - Saya menyalinnya dari pertanyaan awal, yang menunjukkan baris kode berikut: config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();. Sepertinya MVC 4 Web API HttpConfiguration, lihat Bagaimana cara menyetel JsonSerializerSettings kustom untuk Json.NET di MVC 4 Web API? .
dbc
12

Itu jawaban yang sangat bagus. Tapi mengapa tidak mengganti saja ResolveDictionaryKey?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }
Dmitriy
sumber
Jauh lebih singkat. Terima kasih telah berbagi.
Abu Abdullah
1

Jawaban yang dipilih sempurna tetapi saya kira pada saat saya mengetik ini, penyelesai kontrak harus berubah menjadi sesuatu seperti ini karena DictionaryKeyResolver tidak ada lagi :)

public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
    {
        protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
        {
            JsonDictionaryContract contract = base.CreateDictionaryContract(objectType);
            contract.PropertyNameResolver = propertyName => propertyName;
            return contract;
        }
    }
Sep
sumber
5
Sebenarnya kebalikannya benar. Anda harus menggunakan versi lama Json.Net. DictionaryKeyResolverditambahkan di versi 7.0.1 , dan PropertyNameResolverditandai sebagai usang.
Brian Rogers