Управление сериализацией Xml объектов, на которые имеются ссылки, с настраиваемым идентификатором

Краткая версия: мне нужно иметь возможность сериализовать / десериализовать XML и поддерживать ссылки на основе пользовательских идентификаторов, которые я установил. Мне нужно что-то общее, и я могу сделать это, построив ссылки обратно после сериализации, или, желательно, настроить сериализатор C #, который справился бы с этим.

Я создаю приложение WCF, которое должно взаимодействовать с широким спектром приложений. По сути, мы создаем очень сложный калькулятор. У нас есть общая база данных, однако состояние объекта пользователя может отличаться от состояния в БД, и мы не хотим сохранять это промежуточное состояние.

Мне нужно иметь возможность передавать сложные связанные объекты в XML (см. WCF), которые используют настраиваемый построитель ссылок. Строителю необходимо собрать объекты вместе на основе идентификатора каждого объекта. Я хочу иметь возможность указать идентификатор, чтобы другое приложение (например, C ++) могло вызвать наше приложение, правильно построив XML.

Я знаю, что XmlSerializer будет копировать только всю ссылку, а DataContractSerializer сохранит целостность ссылки, но при этом создаст идентификатор на месте. Как разрешить эти ссылки во время сериализации или после сериализации?

Если ответ - «Вы не можете», я задаю следующий вопрос.

В настоящее время мы создали собственный сериализатор, но он работает МЕДЛЕННО (XmlSerializer в 8-30 раз быстрее в зависимости от размера файла). Наш настраиваемый сериализатор может обрабатывать следующий XML, но у него есть второй шаг, на котором он разрешает ссылки (после десериализации). Но мне придется серьезно переработать и настроить, чтобы XmlSerializer работал с этим, потому что нам нужна производительность XmlSerializer. (И.Е. Использование списка идентификаторов и разрешение этих ссылок с помощью XmlIgnore во всех наших ссылочных классах). Существуют ли какие-либо инструменты или библиотеки, которые уже делают это для XML на этапе разрешения или на этапе сериализации?

Изменить: когда я десериализирую это, мне нужно, чтобы эти ссылки сохранялись. Если я меняю имя учителя в классе, имя учителя должно быть изменено в школе.

Я собрал упрощенную версию того, о чем я говорю, и XML, который хотел бы использовать для ее анализа.

public class Teacher
{
    //This is a made up annotation that represents what I'd like to happen.
    [DataMember(IsReferenceId = True)]
    public int Id { get; set; }
    public string Name { get; set; }
    //This is a made up annotation that represents what I'd like to happen.
    [DataMember(IsReference = True)]
    public List<Class> Classes { get; set; }

    public Teacher()
    {
        Classes = new List<Class>();
    }
    public Teacher(int id)
    {
        Classes = new List<Class>();
        Id = id;
    }
}

public class Class
{
    //This is a made up annotation that represents what I'd like to happen.
    [DataMember(IsReferenceId = True)]
    public int Id { get; set; }
    public string Subject { get; set; }
    //This is a made up annotation that represents what I'd like to happen.
    [DataMember(IsReference = True)]
    public Teacher Teacher { get; set; }
    public Class()
    {}

    public Class(int id)
    {
        Id = id;
    }
}

public class School
{
    public string Name { get; set; }
    public List<Class> Classes { get; set; }
    public List<Teacher> Teachers { get; set; }
}

Я хочу иметь возможность анализировать XML таким образом.

<School>
    <Classes>
        <Class>
            <Id>1</Id>
            <Subject>Biology</Subject>
            <Teacher><Id>1</Id></Teacher>
        </Class>
        <Class>
            <Id>2</Id>
            <Subject>Advanced Biology</Subject>
            <Teacher><Id>1</Id></Teacher>
        </Class>        
        <Class>
            <Id>3</Id>
            <Subject>Algebra</Subject>
            <Teacher><Id>2</Id></Teacher>
        </Class>            
        <Class>
            <Id>4</Id>
            <Subject>Trigonometry</Subject>
            <Teacher><Id>2</Id></Teacher>
        </Class>
    </Classes>
    <Teachers>
        <Teacher>
            <Id>1</Id>
            <Name>Biology Teacher</Name>
            <Classes>
                <Class><Id>1</Id></Class>
                <Class><Id>2</Id></Class>
            </Classes>
        </Teacher>
        <Teacher>
            <Id>2</Id>
            <Name>Biology Teacher</Name>
            <Classes>
                <Class><Id>3</Id></Class>
                <Class><Id>4</Id></Class>
            </Classes>
        </Teacher>
    </Teachers>
</School>

Я не включил код, который десериализует или сериализует это, потому что реализация будет зависеть от того, какой сериализатор используется. Но в настоящее время я использую XmlSerializer с [XmlIgnore] вместо [DataContract (IsReference = True)]. Однако мне нужно будет вернуть эти ссылки.

Примечание. JSON - это не вариант, но мы можем использовать любую библиотеку с открытым исходным кодом, которая сериализует Xml.


person thinklarge    schedule 08.07.2015    source источник


Ответы (3)


Вы имеете в виду что-то вроде Persist?:

using System;
using elios.Persist;
using System.Collections.Generic;
using System.IO;

public class Program
{
    public static void Main()
    {
        var students = new List<Student>();

        students.Add(new Student {Name = "Alfred"});
        students.Add(new Student {Name = "Ben"});
        students.Add(new Student {Name = "Camila"});
        students.Add(new Student {Name = "Denise"});

        var alfred = students[0];
        var ben = students[1];
        var camila = students[2];
        var denise = students[3];

        alfred.AddFriend(ben);
        alfred.AddFriend(camila);
        ben.AddFriend(alfred);
        ben.AddFriend(denise);
        camila.AddFriend(alfred);
        camila.AddFriend(ben);
        camila.AddFriend(denise);
        denise.AddFriend(camila);

        var archive = new XmlArchive(typeof(List<Student>));
        string xml;

        using (var s = new MemoryStream())
        {
            archive.Write(s,students,"Students");
            s.Position = 0;

            using (var reader = new StreamReader(s))
            {
                xml = reader.ReadToEnd();
            }   
        }

        Console.WriteLine(xml);

    }
}


public class Student
{
    [Persist("Friends",IsReference = true, ChildName = "Friend")]
    private readonly List<Student> m_friends;

    public string Name { get; set; }


    public Student()
    {
        m_friends = new List<Student>();
    }

    public void AddFriend(Student friend)
    {
        m_friends.Add(friend);
    }
}

Производит:

<Students>
  <Student Name="Alfred" id="4">
    <Friends>
      <Friend id="1" />
      <Friend id="2" />
    </Friends>
  </Student>
  <Student Name="Ben" id="1">
    <Friends>
      <Friend id="4" />
      <Friend id="5" />
    </Friends>
  </Student>
  <Student Name="Camila" id="2">
    <Friends>
      <Friend id="4" />
      <Friend id="1" />
      <Friend id="5" />
    </Friends>
  </Student>
  <Student Name="Denise" id="5">
    <Friends>
      <Friend id="2" />
    </Friends>
  </Student>
</Students>
person elios264    schedule 05.06.2016
comment
примечание: для этого требуются YamlDotNet и .NET Framework ›= 4.5 - person Martin Schneider; 28.05.2018

Попробуй это

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication34
{
    class Program
    {

        static void Main(string[] args)
        {
            string input = 
                 "<School>" +
                    "<Classes>" +
                        "<Class>" +
                            "<Id>1</Id>" +
                            "<Subject>Biology</Subject>" +
                            "<Teacher><Id>1</Id></Teacher>" +
                        "</Class>" +
                        "<Class>" +
                            "<Id>2</Id>" +
                            "<Subject>Advanced Biology</Subject>" +
                            "<Teacher><Id>1</Id></Teacher>" +
                        "</Class>" +        
                        "<Class>" +
                            "<Id>3</Id>" +
                            "<Subject>Algebra</Subject>" +
                            "<Teacher><Id>2</Id></Teacher>" +
                        "</Class>" +
                        "<Class>" +
                            "<Id>4</Id>" +
                            "<Subject>Trigonometry</Subject>" +
                            "<Teacher><Id>2</Id></Teacher>" +
                        "</Class>" +
                    "</Classes>" +
                    "<Teachers>" +
                        "<Teacher>" +
                            "<Id>1</Id>" +
                            "<Name>Biology Teacher</Name>" +
                            "<Classes>" +
                                "<Class><Id>1</Id></Class>" +
                                "<Class><Id>2</Id></Class>" +
                            "</Classes>" +
                        "</Teacher>" +
                        "<Teacher>" +
                            "<Id>2</Id>" +
                            "<Name>Biology Teacher</Name>" +
                            "<Classes>" +
                                "<Class><Id>3</Id></Class>" +
                                "<Class><Id>4</Id></Class>" +
                            "</Classes>" +
                        "</Teacher>" +
                    "</Teachers>" +
                "</School>";

            XDocument doc = XDocument.Parse(input);
            var results = doc.Elements().Select(x => new {
                classes = x.Element("Classes").Elements("Class").Select(y => new {
                    id = y.Element("Id").Value,
                    subject = y.Element("Subject").Value,
                    teacherId = y.Element("Teacher").Element("Id").Value 
                }).ToList(),
                teachers = x.Element("Teachers").Elements("Teacher").Select(y => new {
                    id = y.Element("Id").Value,
                    name = y.Element("Name").Value,
                    classIds = y.Element("Classes").Elements("Class").Select(z => z.Element("Id").Value).ToList()
                }).ToList()
            }).FirstOrDefault();
        }

    }

}
person jdweng    schedule 08.07.2015
comment
Привет, спасибо за ответ, я добавил некоторые пояснения в вопрос, но мне в основном нужно что-то, что поддерживает эти ссылки, чтобы фактически использовать классы, предоставленные после десериализации. Кроме того, мои классы огромны и меняются, поэтому инструмент, который делает это, был бы предпочтительнее. - person thinklarge; 08.07.2015

Вот сериализация. Добавьте идентификацию xml в строку 1 xml:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlSerializer xs = new XmlSerializer(typeof(School));
            XmlTextReader reader = new XmlTextReader(FILENAME);
            School school = (School)xs.Deserialize(reader);
        }
    }
    [XmlRoot("Teachers")]
    public class Teachers
    {
        [XmlElement("Teacher")]
        public List<Teacher> teacher { get; set; }
    }
    [XmlRoot("Teacher")]
    public class Teacher
    {
        //This is a made up annotation that represents what I'd like to happen.
        [XmlElement("Id")]
        public int Id { get; set; }

        [XmlElement("Name")]
        public string Name { get; set; }
        //This is a made up annotation that represents what I'd like to happen.
        [XmlElement("Classes")]
        public List<Classes> Classes { get; set; }
    }
    [XmlRoot("Classes")]
    public class Classes
    {
        [XmlElement("Class")]
        public List<Class> c_class {get; set;} 
    }
    [XmlRoot("Class")]
    public class Class
    {
        //This is a made up annotation that represents what I'd like to happen.
        [XmlElement("Id")]
        public int Id { get; set; }
        [XmlElement("Subject")]
        public string Subject { get; set; }
        //This is a made up annotation that represents what I'd like to happen.
        [XmlElement("Teacher")]
        public Teacher Teacher { get; set; }

    }

    [XmlRoot("School")]
    public class School
    {
        public string Name { get; set; }
        public Classes Classes { get; set; }
        public Teachers Teachers { get; set; }
    }
}
​
person jdweng    schedule 09.07.2015
comment
Извините, но это не ответ на мой вопрос. Я заявляю, что мне действительно нужно что-то, что поддерживает ссылочную целостность. Я даже упомянул, что пробовал XmlSerializer. Это не решит мою проблему с ссылочной целостностью. - person thinklarge; 09.07.2015