เก็บปลอกเมื่อจัดลำดับพจนานุกรม


93

ฉันมีโครงการ Web Api ที่ได้รับการกำหนดค่าดังนี้:

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

อย่างไรก็ตามฉันต้องการให้ตัวครอบแป้นพจนานุกรมไม่เปลี่ยนแปลง มีแอตทริบิวต์ใดบ้างที่Newtonsoft.Jsonฉันสามารถใช้กับคลาสเพื่อแสดงว่าฉันต้องการให้ปลอกยังคงไม่เปลี่ยนแปลงระหว่างการทำให้เป็นอนุกรม

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

1
คุณได้ลองใช้ตัวแก้ไขเริ่มต้นแล้วหรือยัง?
Matthew

1
@ แมทธิวไม่ฉันไม่ได้; คุณช่วยอธิบายด้วยตัวอย่างได้ไหมว่าโค้ดมีลักษณะอย่างไร หมายเหตุฉันยังคงต้องการการทำให้เป็นอนุกรมของเคส Camel สำหรับคำขอเว็บ API ทั้งหมดของฉันฉันแค่ต้องการการจัดลำดับแบบกำหนดเองสำหรับคลาส (หรืออาจจะเป็นคีย์พจนานุกรมใดก็ได้)
zafeiris.m

คำตอบ:


134

ไม่มีแอตทริบิวต์ที่จะทำสิ่งนี้ แต่คุณสามารถทำได้โดยการปรับแต่งตัวแก้ไข

ฉันเห็นว่าคุณใช้ไฟล์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
FYI, PropertyNameResolver ล้าสมัยแล้ว ดูเหมือนว่าจะใช้contract.DictionaryKeyResolver = key => key;งานได้ดี
John Gietzen

1
สิ่งนี้ยังคงเกี่ยวข้องอย่างมากกับประเภทที่ไม่ระบุตัวตนโดยเฉพาะอย่างยิ่งเมื่อเราต้องการปลอกอูฐสำหรับโครงสร้างส่วนใหญ่ แต่ไม่ต้องการให้คีย์ในพจนานุกรมเป็นอูฐ
Chris Schaller

เห็นด้วยกับคริสอย่างแน่นอน ฉันถูกบังคับให้อ่าน hoops ใน JavaScript ของฉันเพียงเพราะฉันไม่สามารถเก็บพจนานุกรมจากการเป็น camelCased ได้ ปรากฎว่าโค้ดหนึ่งบรรทัดจะแก้ปัญหานี้ได้ (และทำให้ JavaScript ของฉันง่ายขึ้นมาก)!
Stephen Chung

@BrianRogers ทำงานได้ดีมาก! อย่างไรก็ตามคุณรู้หรือไม่ว่าฉันสามารถกำหนดเงื่อนไขโดยใช้DictionaryKeyResolverเฉพาะในกรณีที่คุณสมบัติ Dictionary ของฉันมีคุณสมบัติที่กำหนดเอง
Mugen

@Mugen ไม่ได้ปิดด้านบนของหัวของฉัน ฉันอยากจะแนะนำให้ถามเป็นคำถามใหม่ คุณสามารถเชื่อมโยงกลับไปที่คำถามนี้ได้หากคุณต้องการให้บริบท
Brian Rogers

67

Json.NET 9.0.1แนะนำNamingStrategyลำดับชั้นของคลาสเพื่อจัดการปัญหาประเภทนี้ เป็นสารสกัดจากตรรกะสำหรับแมปอัลกอริทึมของชื่อทรัพย์สินจากการจำแนกสัญญาที่จะแยกระดับน้ำหนักเบาที่ช่วยให้การควบคุมว่ากุญแจพจนานุกรม , ชื่อคุณสมบัติระบุไว้อย่างชัดเจนและชื่อข้อมูลส่วนขยาย (ใน10.0.1 ) จะ remapped

การใช้DefaultContractResolverและการตั้งค่าNamingStrategyเป็นอินสแตนซ์ของCamelCaseNamingStrategyคุณสามารถสร้าง JSON ด้วยชื่อคุณสมบัติที่ใส่อูฐและคีย์พจนานุกรมที่ไม่ได้แก้ไขโดยการตั้งค่าในJsonSerializerSettings.ContractResolver:

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. 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ควรเป็นอย่างไร?
Ryan Lundy

@RyanLundy - ฉันคัดลอกมาจากคำถามเริ่มต้นซึ่งแสดงรหัสบรรทัดต่อไปนี้: config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();. ดูเหมือนว่าจะเป็น MVC 4 Web API HttpConfigurationโปรดดูวิธีตั้งค่า JsonSerializerSettings แบบกำหนดเองสำหรับ Json.NET ใน MVC 4 Web API .
dbc

12

นั่นเป็นคำตอบที่ดีมาก แต่ทำไมไม่เพียงแค่ลบล้างResolveDictionaryKey?

class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver
    {
        #region Overrides of DefaultContractResolver

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

        #endregion
    }

สั้นมาก ขอบคุณสำหรับการแบ่งปัน
Abu Abdullah

1

คำตอบที่เลือกนั้นสมบูรณ์แบบ แต่ฉันเดาว่าเมื่อฉันพิมพ์สิ่งนี้ตัวแก้ไขสัญญาต้องเปลี่ยนเป็นแบบนี้เพราะ DictionaryKeyResolver ไม่มีอยู่อีกต่อไป :)

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

5
ที่จริงแล้วสิ่งที่ตรงกันข้ามคือความจริง คุณต้องใช้ Json.Net เวอร์ชันเก่า DictionaryKeyResolverถูกเพิ่มในเวอร์ชัน 7.0.1และPropertyNameResolverถูกทำเครื่องหมายว่าล้าสมัย
Brian Rogers
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.