Json.net serialize / deserialize ประเภทที่ได้รับ?


99

json.net (newtonsoft)
ฉันกำลังดูเอกสาร แต่ไม่พบอะไรเกี่ยวกับสิ่งนี้หรือวิธีที่ดีที่สุดที่จะทำ

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);

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


นั่นไม่ใช่วิธีการทำงานของมรดก คุณสามารถระบุ JsonConvert.Deserialize <Derived> (text); เพื่อรวมฟิลด์ชื่อ เนื่องจาก Derived IS A Base (ไม่ใช่วิธีอื่น) Base ไม่รู้อะไรเกี่ยวกับนิยามของ Derived
M.Babcock

ขออภัยชี้แจงนิดนึง ปัญหาคือฉันมีรายการที่มีทั้งวัตถุฐานและวัตถุที่ได้รับ ดังนั้นฉันต้องหาวิธีที่ฉันบอก newtonsoft ว่าจะแยกรายการที่ได้รับมาอย่างไร
จะ

คุณแก้ปัญหานี้แล้ว ฉันมีปัญหาเดียวกัน
Luis Carlos Chavarría

คำตอบ:


47

หากคุณกำลังจัดเก็บประเภทในของคุณtext(ตามที่คุณควรจะเป็นในสถานการณ์นี้) คุณสามารถใช้ไฟล์JsonSerializerSettings.

ดู: วิธี deserialize JSON ลงใน IEnumerable <BaseType> ด้วย Newtonsoft JSON.NET

ระวังด้วยนะ ใช้อะไรอื่นนอกจากTypeNameHandling = TypeNameHandling.Noneจะเปิดตัวเองถึงช่องโหว่ความปลอดภัย


24
คุณยังสามารถใช้TypeNameHandling = TypeNameHandling.Auto- สิ่งนี้จะเพิ่ม$typeคุณสมบัติเฉพาะสำหรับอินสแตนซ์ที่ประเภทที่ประกาศ (เช่นBase) ไม่ตรงกับประเภทอินสแตนซ์ (เช่นDerived) วิธีนี้จะไม่ขยาย JSON ของคุณมากเท่ากับTypeNameHandling.All.
AJ Richardson

ฉันได้รับข้อผิดพลาดในการแก้ไขประเภทที่ระบุใน JSON '... , ... ' อยู่เรื่อย ๆ เส้นทาง '$ type' บรรทัดที่ 1 ตำแหน่ง 82 ความคิดใด ๆ
briba

3
โปรดใช้ความระมัดระวังเมื่อใช้สิ่งนี้บนปลายทางสาธารณะเนื่องจากจะเปิดปัญหาด้านความปลอดภัย: alphabot.com/security/blog/2017/net/…
gjvdkamp

1
@gjvdkamp JEEZ ขอบคุณสำหรับเรื่องนี้ฉันไม่รู้เกี่ยวกับเรื่องนี้ จะเพิ่มในโพสต์ของฉัน
kamranicus

97

คุณต้องเปิดใช้งาน Type Name Handling และส่งต่อไปยัง (de) serializer เป็นพารามิเตอร์การตั้งค่า

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);

สิ่งนี้จะส่งผลให้มีการดีซีเรียลไลเซชั่นที่ถูกต้องของคลาสที่ได้รับ ข้อเสียเปรียบคือมันจะตั้งชื่อวัตถุทั้งหมดที่คุณใช้ดังนั้นมันจะตั้งชื่อรายการที่คุณใส่วัตถุเข้าไป


31
+1. ฉันใช้ googling เป็นเวลา 30 นาทีจนกระทั่งพบว่าคุณต้องใช้การตั้งค่าเดียวกันสำหรับ SerializeObject & DeserializeObject ฉันคิดว่ามันจะใช้ $ type โดยปริยายถ้ามันอยู่ที่นั่นเมื่อ deserializing ฉันโง่
Erti-Chris Eelmaa

24
TypeNameHandling.Autoจะทำเช่นกันและดีกว่าเพราะไม่ได้เขียนชื่อประเภทอินสแตนซ์เมื่อตรงกับประเภทของฟิลด์ / คุณสมบัติซึ่งมักเป็นกรณีสำหรับฟิลด์ / คุณสมบัติส่วนใหญ่
Roman Starkov

2
สิ่งนี้ใช้ไม่ได้เมื่อทำการ deserialization กับโซลูชัน / โครงการอื่น ในการทำให้เป็นอนุกรมชื่อของโซลูชันจะถูกฝังอยู่ในประเภท: "SOLUTIONNAME.Models.Model" ในการ deserialization ในโซลูชันอื่นจะแสดงข้อความ "JsonSerializationException: ไม่สามารถโหลดแอสเซมบลี 'SOLUTIONNAME' ได้
Jebathon

19

เนื่องจากคำถามเป็นที่นิยมมากจึงอาจมีประโยชน์ในการเพิ่มสิ่งที่ต้องทำถ้าคุณต้องการควบคุมชื่อคุณสมบัติประเภทและค่าของมัน

ทางยาวคือการเขียน custom JsonConverters เพื่อจัดการ (de) serialization โดยการตรวจสอบและตั้งค่าคุณสมบัติ type ด้วยตนเอง

วิธีที่ง่ายกว่าคือใช้JsonSubTypesซึ่งจัดการสำเร็จรูปทั้งหมดผ่านแอตทริบิวต์:

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}

4
ฉันได้รับความต้องการ แต่ฉันไม่ใช่แฟนตัวยงที่ต้องทำให้คนชั้นเรียนรู้ถึง "KnownSubType" ทั้งหมด ...
Matt Knowles

2
มีตัวเลือกอื่น ๆ หากคุณดูเอกสารประกอบ ฉันให้เฉพาะตัวอย่างที่ฉันชอบมากกว่า
rzippo

1
นี่เป็นวิธีการที่ปลอดภัยกว่าซึ่งจะไม่เปิดเผยให้บริการของคุณโหลดประเภทตามอำเภอใจเมื่อยกเลิกการทำให้เป็นอนุกรม
David Burg

3

ใช้JsonKnownTypesนี้ซึ่งเป็นวิธีการใช้ที่คล้ายกันมากเพียงแค่เพิ่มตัวเลือกให้กับ json:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

ตอนนี้เมื่อคุณทำให้เป็นอนุกรมวัตถุใน json จะถูกเพิ่ม"$type"ด้วย"base"และ"derived"ค่าและจะใช้สำหรับการแยกส่วน

ตัวอย่างรายการอนุกรม:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.