【问题标题】:XmlSerializer access child logical elements in parent elementXmlSerializer 访问父元素中的子逻辑元素
【发布时间】:2020-10-05 19:33:39
【问题描述】:

我正在使用标准 .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; }
}

这当然行不通,因为 - 例如 - &lt;Item1&gt; 不在 &lt;Element&gt; 中,而是在逻辑容器 &lt;Grouping1&gt; 中。

问题:

有没有办法告诉XmlSerializer&lt;Grouping1&gt; 元素中寻找Item1
[XmlElement("Grouping1.Item1")] 的行有问题


PS:我不想创建 Grouping1 类(建议 here),因为分组只是逻辑容器,不应该有自己的类。

【问题讨论】:

  • 您可以使用 Xml Linq 创建类。序列化您必须创建一个自定义序列化。 XML 序列化需要一个类或元素用于每个唯一的标记名称。您有 Grouping1 和 Grouping2 需要两个类。您不能使用 Xml 序列化将 Grouping1 和 Grouping2 的子级归为一个类。
  • @jdweng 感谢您的回答!我当前的解决方案是删除&lt;Grouping1&gt;&lt;Grouping2&gt; 标记,直到Grouping 元素有一个具有通用名称的子元素之前都可以正常工作。你知道如何克服这个问题吗?
  • 您确定元素名称吗? Grouping1,2,3Item1,2,3 ?不应该只是GroupingItem 吗?
  • @JQSOFT 我不太明白你想要什么,但我的问题可能会更好地可视化hereGrouping 1 - n 应该只是逻辑分组的占位符(就像上面示例中的 Name 一样)。 Item 也是如此,他们只是为某个属性重新命名。希望这有助于澄清

标签: c# xml serialization xml-serialization


【解决方案1】:

使用 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);
        }
    }


}

【讨论】:

  • 对于这个小示例范围,这是一个可行的解决方案,是的。可悲的是,我在现实生活中的用例使用了 60 多个元素,但它并不是为所有元素编写自定义映射的实用解决方案。因此需要使用属性。
  • 解析 xml 的方法有很多种,您必须查看整个解决方案才能选择最佳解决方案。 Xml Linq 很好,因为 ti 处理非标准的 xml 文件,并且会给出比标准序列化更平坦的结果。
  • 我更新了代码以显示自定义序列化的样子。
【解决方案2】:

我不想创建 Grouping1 类...

不确定是否可以在不创建输出此 XML 的对象的情况下使用序列化。

序列化的替代方法是使用XmlReader提取有问题的属性(Item1Item2Item3)并创建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

【讨论】:

  • 感谢您的回答! jdweng 的解决方案与此类似 - 简而言之:custommappig。正如我在他的帖子中所解释的那样,对于我的用例来说,这并不是一个可行的解决方案,很遗憾:/
  • 感谢您的高质量回答!
  • @Squirrelintraining 最欢迎的朋友。至于您在帖子中提到的 逻辑容器,这并不意味着分组元素在 XML 中没有物理存在,这意味着您不应该忽略这一点并做一些事情它。此外,如果您可以为几个元素粘贴一个真正的 XML,这样我们就可以更好地挖掘,也许会出现更好的解决方案。
  • 抱歉,不能那样做^^' 但是要有一个想法,想象 有 70 个项目 ()。并且 偶尔会有大部分不同的 ,有时还有一个具有相同名称但值不同的
  • @Squirrelintraining 现在我们有一些更好的信息可以考虑,这意味着这两个答案都不起作用。最后一个问题要在同一页上。你有多个&lt;Element&gt;..&lt;/Element&gt;吗?和Grouping3, Grouping4 ,...等等?
猜你喜欢
  • 1970-01-01
  • 2018-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
相关资源
最近更新 更多