DataContract serializer ใช้งานไม่ได้กับประเภทที่คล้ายกัน

ฉันมีแอปพลิเคชันที่ต้องใช้ตัวซีเรียลไลเซอร์สัญญาข้อมูลสำหรับประเภทที่แตกต่างกันแต่คล้ายกัน:

  • รายการทั่วไป
  • คอลเลกชันทั่วไป
  • อาร์เรย์

ตามคำขอจาก Scrobi นี่เป็นตัวอย่างแบบเต็ม:

[DataContract]
public class Model
{
    public Model()
    {
        List = new List<int>(new[] {1, 2, 3});
        Collection = new Collection<int>(new[] {4, 5, 6});
        Array = new[] {7, 8, 9};
    }

    [DataMember]
    public object List { get; set; }

    [DataMember]
    public object Collection { get; set; }

    [DataMember]
    public object Array { get; set; }

    public string SerializeMe()
    {
        var serializer = new DataContractSerializer(typeof(Model), GetKnownTypes());

        using (var stream = new MemoryStream())
        {
            serializer.WriteObject(stream, this); // exception

            return Encoding.UTF8.GetString(stream.GetBuffer());
        }
    }

    public static Type[] GetKnownTypes()
    {
        return new[]
        {
            typeof(List<int>),
            typeof(Collection<int>), // error
            typeof(int[]) // error
        };
    }
}

ปัญหาคือ: ฉันไม่สามารถเพิ่มรายการทั่วไป คอลเลกชัน และอาร์เรย์พร้อมกันได้ เนื่องจากทั้งหมดใช้สัญญาข้อมูลเดียวกัน

เมื่อฉันใช้คอลเลกชั่นประเภทใดประเภทหนึ่งเท่านั้น ฉันจะไม่สามารถซีเรียลไลซ์ประเภทอื่นได้ เนื่องจากไม่ทราบ (มีสัญญาข้อมูลอยู่ แต่มีประเภทอื่น) ใช่ ฟิลด์ต่างๆ จะต้องเป็นอ็อบเจ็กต์เพื่อให้ใช้งานได้ในกรณีของฉัน (และในแอปพลิเคชันจริง ฉันไม่สามารถเพิ่มแอตทริบิวต์ให้กับฟิลด์เหล่านั้นได้)

เป็นไปไม่ได้เลยในแอปพลิเคชันที่ฉันเขียนเพื่อใช้ประเภทใดประเภทหนึ่งเท่านั้น เนื่องจากเป็นสภาพแวดล้อมการพัฒนาประเภทหนึ่งและนักพัฒนาสามารถเลือกประเภทได้อย่างอิสระ

มีวิธีแก้ไขชั่วคราวสำหรับข้อจำกัดนี้ของตัวซีเรียลไลเซอร์สัญญาข้อมูลหรือไม่

ขอบคุณ,


person knurd nerd    schedule 03.07.2017    source แหล่งที่มา
comment
ตามแหล่งที่มานี้. คอลเลกชันรายการทั้งหมดที่เป็นประเภทเดียวกันจะถือว่ามีสัญญาข้อมูลเดียวกัน ดังนั้น List‹int› และ Collection‹int› จึงเทียบเท่ากัน   -  person Scrobi    schedule 03.07.2017
comment
แน่นอน. แต่คุณยังคงไม่สามารถทำให้รายการเป็นอนุกรมได้เมื่อคุณเพิ่มคอลเลกชันลงในประเภทที่รู้จัก   -  person knurd nerd    schedule 03.07.2017
comment
บางทีตัวอย่างของคุณอาจเรียบง่าย แต่ทำไมคุณถึงเพิ่ม Collection<int> ให้กับประเภทที่รู้จัก หากคอลเลกชันทั้งหมดเทียบเท่าและแลกเปลี่ยนได้แล้ว?   -  person Scrobi    schedule 03.07.2017
comment
เพราะเมื่อคุณเพิ่ม Collection‹int› และพยายามทำให้เป็นอนุกรม List‹int› มันจะไม่ทำงาน   -  person knurd nerd    schedule 03.07.2017
comment
ฉันคิดว่ามันจะเป็นประโยชน์หากคุณเพิ่มโมเดลของคุณที่คุณใช้คอลเลกชั่นต่างๆ   -  person Scrobi    schedule 03.07.2017
comment
ขอบคุณสำหรับการติดตาม. ฉันเพิ่มตัวอย่างแบบเต็ม   -  person knurd nerd    schedule 03.07.2017
comment
นี่เป็นปัญหาที่เทียบเท่ากับ XmlSerializer: XmlInclude : รายการและอาร์เรย์ อาจเป็นวิธีแก้ปัญหาที่คล้ายกันก็ใช้ได้ผลที่นี่เช่นกัน   -  person dbc    schedule 03.07.2017
comment
คุณเขียนว่า และในการใช้งานจริง ฉันไม่สามารถเพิ่มคุณลักษณะลงไปได้ นั่นหมายความว่าคุณไม่สามารถเปลี่ยนประเภทที่กำลังซีเรียลไลซ์ได้เลยใช่ไหม   -  person dbc    schedule 04.07.2017


คำตอบ (1)


ฉันมีคำตอบบางส่วน แต่ด้วยเหตุผลบางอย่างเมื่อทดสอบ Collection<int> ทำให้เกิดข้อผิดพลาดเมื่อทำการดีซีเรียลไลซ์ บางทีคุณอาจใช้สิ่งนี้เพื่อค้นหาวิธีแก้ปัญหาที่สมบูรณ์ได้

ฉันสร้าง DataContractResolver เพื่อให้คุณสามารถแทนที่ xsi:type ได้ มีเอกสารบางอย่าง ที่นี่

public class MyResolver : DataContractResolver
{
    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        string name = string.Empty;
        bool isFound = false;
        if (type.Name == "Int32[]")
        {
            name = "IntArray";
            isFound = true;
        }
        if (type.Name.Contains("List") && type.FullName.Contains("Int")) //find List<int>
        {
            name = "IntList";
            isFound = true;
        }
        if (type.Name.Contains("Collection") && type.FullName.Contains("Int")) //find Collection<int> 
        {
            name = "IntCollection";
            isFound = true;
        }

        if (isFound)
        {
            XmlDictionary dictionary = new XmlDictionary();
            typeName = dictionary.Add(name);
            typeNamespace = dictionary.Add("http://tempuri.com");
            return true;
        }

        return knownTypeResolver.TryResolveType(type, declaredType, knownTypeResolver, out typeName, out typeNamespace);
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        if (typeName == "IntArray" )
        {
            return typeof(int[]);
        }
        if (typeName == "IntList")
        {
            return typeof(List<int>);
        }
        if (typeName == "IntCollection")
        {
            return typeof(Collection<int>);
        }


        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
    }
}

จากนั้นคุณไม่จำเป็นต้อง GetKnownTypes() ของคุณและสร้าง DataContractSerializer ดังนี้:

var serializer = new DataContractSerializer(typeof(Model),null, int.MaxValue, false, false,null, new MyResolver());

หวังว่านี่จะช่วยได้บ้าง

person Scrobi    schedule 04.07.2017
comment
ยอดเยี่ยม! ดูเหมือนสิ่งที่ฉันกำลังมองหาและให้คำแนะนำแก่ฉันอย่างเพียงพอในทิศทางที่ถูกต้อง ขอบคุณมาก :-) - person knurd nerd; 04.07.2017