XmlSerializer получает доступ к дочерним логическим элементам в родительском элементе - PullRequest
1 голос
/ 16 июня 2020

Я использую стандарт. NET XmlSerializer для десериализации следующего xml:

<root>
    <Element>
        <Grouping1>
            <Item1>First</Item1>
            <Item2>Second</Item2>
        </Grouping1>
        <Grouping2>
            <Item3>Third</Item3>
        </Grouping2>
    </Element>
</root>

Я хотел бы сериализовать его в следующий класс:

class Element
{
    [XmlElement("Item1")]
    public string Item1 { get; set; }

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

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

Что, конечно, не работает, потому что, например, <Item1> находится не в <Element>, а в логическом контейнере <Grouping1>.

Вопрос:

Есть ли способ указать XmlSerializer искать Item1 в элементе <Grouping1>?
Что-то вниз по строкам [XmlElement("Grouping1.Item1")]


PS: я не хочу создавать класс Grouping1 (как предлагается здесь ), потому что группировки являются только логическими контейнерами и не должны иметь своего собственного класса.

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Я не хочу создавать класс Grouping1 ...

Не уверен, возможно ли это с помощью сериализации без создания объектов, которые выводят это XML.

Альтернативой сериализации является использование XmlReader от до извлечения соответствующих свойств (Item1, Item2 и Item3) и создания список типа Element и XmlWriter для генерации всего файла XML. Оба класса обеспечивают быстрый, не кэшированный и прямой способ чтения и записи XML файлов.

Предполагается, что ваш XML файл содержит несколько Element записей, например:

<root>
  <Element>
    <Grouping1>
      <Item1>First1</Item1>
      <Item2>Second1</Item2>
    </Grouping1>
    <Grouping2>
      <Item3>Third1</Item3>
    </Grouping2>
  </Element>
  <Element>
    <Grouping1>
      <Item1>First2</Item1>
      <Item2>Second2</Item2>
    </Grouping1>
    <Grouping2>
      <Item3>Third2</Item3>
    </Grouping2>
  </Element>
</root>

... и класс с именем Element:

//The serializable attribute is not required here...
public class Element
{
    public Element() { }

    public string Item1 { get; set; }
    public string Item2 { get; set; }
    public string Item3 { get; set; }

    public override string ToString() => $"{Item1}, {Item2}, {Item3}";
}

Создайте функцию для чтения файла, создания и возврата списка Element элементов:

public List<Element> ReadElements(string xmlFile)
{
    var elements = new List<Element>();
    Element ele = null;

    using (var xr = XmlReader.Create(xmlFile))
        while (xr.Read())
        {
            if (xr.NodeType == XmlNodeType.Element)
            {
                if (xr.Name == "Element")
                    ele = new Element();
                else if (xr.Name == "Item1")
                {
                    xr.Read();
                    ele.Item1 = xr.Value;
                }
                else if (xr.Name == "Item2")
                {
                    xr.Read();
                    ele.Item2 = xr.Value;
                }
                else if (xr.Name == "Item3")
                {
                    xr.Read();
                    ele.Item3 = xr.Value;
                }
            }
            else if (xr.NodeType == XmlNodeType.EndElement)
                if (xr.Name == "Element")
                    elements.Add(ele);
        }
    return elements;
}

... и метод записи:

public void WriteElements(string xmlFile, IEnumerable<Element> elements)
{
    var xmlSet = new XmlWriterSettings
    {
        Indent = true,
        NewLineOnAttributes = true,
        WriteEndDocumentOnClose = true,
    };

    using (var xr = XmlWriter.Create(xmlFile, xmlSet))
    {
        xr.WriteStartElement("root");                

        foreach(var ele in elements)
        {
            xr.WriteStartElement("Element");
            xr.WriteStartElement("Grouping1");
            xr.WriteStartElement("Item1");
            xr.WriteString(ele.Item1);
            xr.WriteEndElement();
            xr.WriteStartElement("Item2");
            xr.WriteString(ele.Item2);
            xr.WriteEndElement();
            xr.WriteEndElement();
            xr.WriteStartElement("Grouping2");
            xr.WriteStartElement("Item3");
            xr.WriteString(ele.Item3);
            xr.WriteEndElement();
            xr.WriteEndElement();
            xr.WriteEndElement();
        }
    }            
}

Тест для чтения и записи файла, например:

private void TheCaller()
{
    var xmlFile = "XmlFile.xml";
    var elements = ReadElements(xmlFile);
    elements.ForEach(x => Console.WriteLine(x));
    //...
    WriteElements(xmlFile, elements);
}

Выводит в моем окне вывода:

First1, Second1, Third1
First2, Second2, Third2
1 голос
/ 16 июня 2020

Использование Xml Linq. Пользовательский сериализатор будет намного сложнее

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

namespace ConsoleApplication4
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            List<Element> elements = doc.Descendants("Element").Select(x => new Element()
            {
                Item1 = (string)x.Descendants("Item1").FirstOrDefault(),
                Item2 = (string)x.Descendants("Item2").FirstOrDefault(),
                Item3 = (string)x.Descendants("Item3").FirstOrDefault()
            }).ToList();

         }
    }
    class Element
    {
        public string Item1 { get; set; }
        public string Item2 { get; set; }
        public string Item3 { get; set; }
    }


}

Вот как будет выглядеть сериализатор

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

namespace ConsoleApplication4
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Root));
            Root root = (Root)serializer.Deserialize(reader);


         }
    }
    [XmlRoot("root")]
    public class Root
    {
        [XmlElement("Element")]
        public List<Element> Element { get; set; }
    }

    public class Element : IXmlSerializable
    {
        private string Item1 { get; set; }
        private string Item2 { get; set; }
        private string Item3 { get; set; }

        public void WriteXml(XmlWriter writer)
        {
            XElement element = new XElement("Element", new object[] {
                new XElement("Grouping1", new object[] {
                    new XElement("Item1", Item1),
                    new XElement("Item2", Item2)
                }),
                new XElement("Grouping2", new XElement("Item3", Item3))
            });
            element.WriteTo(writer);

        }

        public void ReadXml(XmlReader reader)
        {
            XElement element = (XElement)XElement.ReadFrom(reader);

            Item1 = (string)element.Descendants("Item1").FirstOrDefault();
            Item2 = (string)element.Descendants("Item2").FirstOrDefault();
            Item3 = (string)element.Descendants("Item3").FirstOrDefault();

        }

        public XmlSchema GetSchema()
        {
            return (null);
        }
    }


}
...