Programing

XML 직렬화 직렬화 가능한 개체의 일반 목록

crosscheck 2020. 10. 30. 07:44
반응형

XML 직렬화 직렬화 가능한 개체의 일반 목록


유형을 지정하지 않고도 직렬화 가능한 객체의 일반 목록을 직렬화 할 수 있습니까?

아래 깨진 코드의 의도와 같은 것 :

List<ISerializable> serializableList = new List<ISerializable>();

XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());

serializableList.Add((ISerializable)PersonList);

using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xmlSerializer.Serialize(streamWriter, serializableList);
}

편집하다:

세부 사항을 알고 싶은 사람들을 위해 :이 코드를 실행하려고하면 XMLSerializer [...] 줄에서 다음과 같은 오류가 발생합니다.

System.Runtime.Serialization.ISerializable 인터페이스를 직렬화 할 수 없습니다.

내가로 변경 List<object>하면 "There was an error generating the XML document.". InnerException 세부 사항은 다음과 같습니다."{"The type System.Collections.Generic.List1[[Project1.Person, ConsoleFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] may not be used in this context."}"

사람 개체는 다음과 같이 정의됩니다.

[XmlRoot("Person")]
public class Person
{
    string _firstName = String.Empty;
    string _lastName = String.Empty;

    private Person()
    {
    }

    public Person(string lastName, string firstName)
    {
        _lastName = lastName;
        _firstName = firstName;
    }

    [XmlAttribute(DataType = "string", AttributeName = "LastName")]
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    [XmlAttribute(DataType = "string", AttributeName = "FirstName")]
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
}

PersonList는 List<Person>.

이것은 단지 테스트 용이므로 세부 사항이 너무 중요하다고 생각하지 않았습니다. 핵심은 하나 이상의 다른 객체가 있으며 모두 직렬화 가능하다는 것입니다. 나는 그것들을 모두 하나의 파일로 직렬화하고 싶습니다. 이를 수행하는 가장 쉬운 방법은 일반 목록에 넣고 한 번에 목록을 직렬화하는 것입니다. 그러나 이것은 작동하지 않습니다.

나도 시도 List<IXmlSerializable>했지만 실패했습니다.

System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.

디테일이 부족해서 미안하지만 저는 이것에 초보자이며 어떤 디테일이 필요한지 모르겠습니다. 더 자세한 정보를 요청하는 사람들이 어떤 세부 정보가 필요한지 이해할 수있는 방식으로 응답하거나 가능한 방향을 설명하는 기본 답변을 제공하면 도움이 될 것입니다.

또한 지금까지 얻은 두 가지 답변 덕분 에 이러한 아이디어를 얻지 못한 채 더 많은 시간을 읽을 수있었습니다. 사람들이이 사이트에 얼마나 도움이되는지 놀랍습니다.


동적 바인딩 항목이있는 일반 List <>에 대한 솔루션이 있습니다.

PersonalList 클래스는 루트 요소입니다.

[XmlRoot("PersonenListe")]
[XmlInclude(typeof(Person))] // include type class Person
public class PersonalList
{
    [XmlArray("PersonenArray")]
    [XmlArrayItem("PersonObjekt")]
    public List<Person> Persons = new List<Person>();

    [XmlElement("Listname")]
    public string Listname { get; set; }

    // Konstruktoren 
    public PersonalList() { }

    public PersonalList(string name)
    {
        this.Listname = name;
    }

    public void AddPerson(Person person)
    {
        Persons.Add(person);
    }
}

Person 클래스는 단일 목록 요소입니다.

[XmlType("Person")] // define Type
[XmlInclude(typeof(SpecialPerson)), XmlInclude(typeof(SuperPerson))]  
        // include type class SpecialPerson and class SuperPerson
public class Person
{
    [XmlAttribute("PersID", DataType = "string")]
    public string ID { get; set; }

    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("City")]
    public string City { get; set; }

    [XmlElement("Age")]
    public int Age { get; set; }

    // Konstruktoren 
    public Person() { }

    public Person(string name, string city, int age, string id)
    {
        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
    }
}

SpecialPerson 클래스는 Person을 상속합니다.

[XmlType("SpecialPerson")] // define Type
public class SpecialPerson : Person
{
    [XmlElement("SpecialInterests")]
    public string Interests { get; set; }

    public SpecialPerson() { }

    public SpecialPerson(string name, string city, int age, string id, string interests)
    {
        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
        this.Interests = interests;
    }
}

SuperPerson 클래스는 Person을 상속합니다.

[XmlType("SuperPerson")] // define Type
public class SuperPerson : Person
{
    [XmlArray("Skills")]
    [XmlArrayItem("Skill")]
    public List<String> Skills { get; set; }

    [XmlElement("Alias")]
    public string Alias { get; set; }

    public SuperPerson() 
    {
        Skills = new List<String>();
    }

    public SuperPerson(string name, string city, int age, string id, string[] skills, string alias)
    {
        Skills = new List<String>();

        this.Name = name;
        this.City = city;
        this.Age = age;
        this.ID = id;
        foreach (string item in skills)
        {
            this.Skills.Add(item);   
        }
        this.Alias = alias;
    }
}

및 주요 테스트 소스

static void Main(string[] args)
{
    PersonalList personen = new PersonalList(); 
    personen.Listname = "Friends";

    // normal person
    Person normPerson = new Person();
    normPerson.ID = "0";
    normPerson.Name = "Max Man";
    normPerson.City = "Capitol City";
    normPerson.Age = 33;

    // special person
    SpecialPerson specPerson = new SpecialPerson();
    specPerson.ID = "1";
    specPerson.Name = "Albert Einstein";
    specPerson.City = "Ulm";
    specPerson.Age = 36;
    specPerson.Interests = "Physics";

    // super person
    SuperPerson supPerson = new SuperPerson();
    supPerson.ID = "2";
    supPerson.Name = "Superman";
    supPerson.Alias = "Clark Kent";
    supPerson.City = "Metropolis";
    supPerson.Age = int.MaxValue;
    supPerson.Skills.Add("fly");
    supPerson.Skills.Add("strong");

    // Add Persons
    personen.AddPerson(normPerson);
    personen.AddPerson(specPerson);
    personen.AddPerson(supPerson);

    // Serialize 
    Type[] personTypes = { typeof(Person), typeof(SpecialPerson), typeof(SuperPerson) };
    XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes); 
    FileStream fs = new FileStream("Personenliste.xml", FileMode.Create); 
    serializer.Serialize(fs, personen); 
    fs.Close(); 
    personen = null;

    // Deserialize 
    fs = new FileStream("Personenliste.xml", FileMode.Open); 
    personen = (PersonalList)serializer.Deserialize(fs); 
    serializer.Serialize(Console.Out, personen);
    Console.ReadLine();
}

중요한 것은 다른 유형의 정의와 포함입니다.


XML 직렬화 소개를 참조하십시오 .

직렬화 할 수있는 항목

XmlSerializer 클래스를 사용하여 다음 항목을 직렬화 할 수 있습니다 .

  • 공용 클래스의 공용 읽기 / 쓰기 속성 및 필드
  • 구현하는 클래스 ICollection또는IEnumerable
  • XmlElement 사물
  • XmlNode 사물
  • DataSet 사물

특히 ISerializable또는 [Serializable]속성은 중요하지 않습니다.


이제 문제가 무엇인지 알려 주셨으므로 ( "작동하지 않습니다"는 문제 설명이 아님) 추측 대신 실제 문제에 대한 답을 얻을 수 있습니다.

형식의 컬렉션을 serialize하지만 실제로 파생 형식의 인스턴스 컬렉션을 serialize 할 경우 serializer에 실제로 serialize 할 형식을 알려야합니다. 이는 object.

가능한 형식 목록을 제공하려면 XmlSerializer (Type, Type []) 생성자 를 사용해야합니다 .


예상 유형을 지정하지 않고는 개체 컬렉션을 직렬화 할 수 없습니다. 예상 유형 목록을 XmlSerializer( extraTypes매개 변수) 의 생성자에 전달해야합니다 .

List<object> list = new List<object>();
list.Add(new Foo());
list.Add(new Bar());

XmlSerializer xs = new XmlSerializer(typeof(object), new Type[] {typeof(Foo), typeof(Bar)});
using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xs.Serialize(streamWriter, list);
}

목록의 모든 객체가 동일한 클래스에서 상속되는 경우 XmlInclude속성을 사용하여 예상 유형을 지정할 수도 있습니다.

[XmlInclude(typeof(Foo)), XmlInclude(typeof(Bar))]
public class MyBaseClass
{
}

다음과 같은 일반적인 인수와 함께 메서드를 사용하는 것이 가장 좋습니다.

public static void SerializeToXml<T>(T obj, string fileName)
{
    using (var fileStream = new FileStream(fileName, FileMode.Create))
    { 
        var ser = new XmlSerializer(typeof(T)); 
        ser.Serialize(fileStream, obj);
    }
}

public static T DeserializeFromXml<T>(string xml)
{
    T result;
    var ser = new XmlSerializer(typeof(T));
    using (var tr = new StringReader(xml))
    {
        result = (T)ser.Deserialize(tr);
    }
    return result;
}

Dreas의 접근 방식이 괜찮다고 생각합니다. 그러나 이에 대한 대안은 몇 가지 정적 도우미 메서드를 갖고 각 메서드에 IXmlSerializable을 구현하는 것입니다 (예 : XmlWriter 확장 메서드 및 XmlReader 메서드).

public static void SaveXmlSerialiableElement<T>(this XmlWriter writer, String elementName, T element) where T : IXmlSerializable
{
   writer.WriteStartElement(elementName);
   writer.WriteAttributeString("TYPE", element.GetType().AssemblyQualifiedName);
   element.WriteXml(writer);
   writer.WriteEndElement();
}

public static T ReadXmlSerializableElement<T>(this XmlReader reader, String elementName) where T : IXmlSerializable
{
   reader.ReadToElement(elementName);

   Type elementType = Type.GetType(reader.GetAttribute("TYPE"));
   T element = (T)Activator.CreateInstance(elementType);
   element.ReadXml(reader);
   return element;
}

XmlSerializer 클래스를 직접 사용하는 경로를 따라가는 경우 새 XmlSerializer를 정기적으로 생성하는 데 큰 성능 저하가 발생할 수 있으므로 가능하면 미리 직렬화 어셈블리를 만듭니다.

컬렉션의 경우 다음과 같은 것이 필요합니다.

public static void SaveXmlSerialiazbleCollection<T>(this XmlWriter writer, String collectionName, String elementName, IEnumerable<T> items) where T : IXmlSerializable
{
   writer.WriteStartElement(collectionName);
   foreach (T item in items)
   {
      writer.WriteStartElement(elementName);
      writer.WriteAttributeString("TYPE", item.GetType().AssemblyQualifiedName);
      item.WriteXml(writer);
      writer.WriteEndElement();
   }
   writer.WriteEndElement();
}

아래는 내 프로젝트의 Util 클래스입니다.

namespace Utils
{
    public static class SerializeUtil
    {
        public static void SerializeToFormatter<F>(object obj, string path) where F : IFormatter, new()
        {
            if (obj == null)
            {
                throw new NullReferenceException("obj Cannot be Null.");
            }

            if (obj.GetType().IsSerializable == false)
            {
                //  throw new 
            }
            IFormatter f = new F();
            SerializeToFormatter(obj, path, f);
        }

        public static T DeserializeFromFormatter<T, F>(string path) where F : IFormatter, new()
        {
            T t;
            IFormatter f = new F();
            using (FileStream fs = File.OpenRead(path))
            {
                t = (T)f.Deserialize(fs);
            }
            return t;
        }

        public static void SerializeToXML<T>(string path, object obj)
        {
            XmlSerializer xs = new XmlSerializer(typeof(T));
            using (FileStream fs = File.Create(path))
            {
                xs.Serialize(fs, obj);
            }
        }

        public static T DeserializeFromXML<T>(string path)
        {
            XmlSerializer xs = new XmlSerializer(typeof(T));
            using (FileStream fs = File.OpenRead(path))
            {
                return (T)xs.Deserialize(fs);
            }
        }

        public static T DeserializeFromXml<T>(string xml)
        {
            T result;

            var ser = new XmlSerializer(typeof(T));
            using (var tr = new StringReader(xml))
            {
                result = (T)ser.Deserialize(tr);
            }
            return result;
        }


        private static void SerializeToFormatter(object obj, string path, IFormatter formatter)
        {
            using (FileStream fs = File.Create(path))
            {
                formatter.Serialize(fs, obj);
            }
        }
    }
}

내가 찾은 가장 쉬운 방법은 .. System.Xml.Serialization.XmlArray속성을 적용 합니다.

[System.Xml.Serialization.XmlArray] //This is the part that makes it work
List<object> serializableList = new List<object>();

XmlSerializer xmlSerializer = new XmlSerializer(serializableList.GetType());

serializableList.Add(PersonList);

using (StreamWriter streamWriter = System.IO.File.CreateText(fileName))
{
    xmlSerializer.Serialize(streamWriter, serializableList);
}

The serializer will pick up on it being an array and serialize the list's items as child nodes.


knowTypeList parameter let serialize with DataContractSerializer several known types:

private static void WriteObject(
        string fileName, IEnumerable<Vehichle> reflectedInstances, List<Type> knownTypeList)
    {
        using (FileStream writer = new FileStream(fileName, FileMode.Append))
        {
            foreach (var item in reflectedInstances)
            {
                var serializer = new DataContractSerializer(typeof(Vehichle), knownTypeList);
                serializer.WriteObject(writer, item);
            }
        }
    }

If the XML output requirement can be changed you can always use binary serialization - which is better suited for working with heterogeneous lists of objects. Here's an example:

private void SerializeList(List<Object> Targets, string TargetPath)
{
    IFormatter Formatter = new BinaryFormatter();

    using (FileStream OutputStream = System.IO.File.Create(TargetPath))
    {
        try
        {
            Formatter.Serialize(OutputStream, Targets);
        } catch (SerializationException ex) {
            //(Likely Failed to Mark Type as Serializable)
            //...
        }
}

Use as such:

[Serializable]
public class Animal
{
    public string Home { get; set; }
}

[Serializable]
public class Person
{
    public string Name { get; set; }
}


public void ExampleUsage() {

    List<Object> SerializeMeBaby = new List<Object> {
        new Animal { Home = "London, UK" },
        new Person { Name = "Skittles" }
    };

    string TargetPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "Test1.dat");

    SerializeList(SerializeMeBaby, TargetPath);
}

참고URL : https://stackoverflow.com/questions/1212742/xml-serialize-generic-list-of-serializable-objects

반응형