如何将子元素移动到父元素(XML)的属性

时间:2018-08-08 15:55:53

标签: c# xml xml-serialization

我目前有一个XML文件,它的大小相当大(大约800MB)。我尝试了一些尝试(here是处理压缩的一种方法),以使其在当前条件下工作;但是,由于花费了一些时间,他们并不是很成功。

XML的文件结构类似于以下内容(这一代早于我):

<Name>Something</Name>
<Description>Some description.</Description>
<CollectionOfObjects>
    <Object>
        <Name>Name Of Object</Name>
        <Description>Description of object.</Description>
        <AltName>Alternate name</AltName>
        <ContainerName>Container</ContainerName>
        <Required>true</Required>
        <Length>1</Length>
            <Info>
                <Name>Name</Name>
                <File>Filename</File>
                <Size>20</Size>
                <SizeUnit>MB</SizeUnit>
            </Info>
    </Object>
</CollectionOfObjects>

每个对象下都有大量数据,这些子节点中的许多子节点都可以作为其父节点的属性:

<CollectionOfObjects Name="Something" Description="Some description.">
    <Object Name="Name Of Object" AltName="Alternate name" Container="Container" Required="true" Length="1" Description="Description of object.">
            <Info Name="Name" File="Filename" Size="20" SizeUnit="MB" />
    </Object>
</CollectionOfObjects>

现在,显然不是每个节点下的所有内容都将成为属性;以上只是一个例子。该文件中的数据太多,导致Notepad中断,Visual Studio大约需要2分钟才能打开。如果您尝试搜索文件,因为它需要一个小时或更长时间,Heaven会为您提供帮助。

您可以看到这有什么问题。我已经对大小差异进行了测试(显然不是使用此文件),而是使用了演示文件。我创建了一个文件,并将不必要的子节点转换为属性,这使演示文件的大小减少了53%。我毫不怀疑,对此文件执行相同的工作将使其大小减小30%或更多(希望更多)。

现在您了解了为什么,让我们开始思考这个问题;如何将这些子节点移至属性。该文件是通过XmlSerializer生成的,并使用反射根据可用的类和属性来构建节点:

internal class DemoClass {
    [CategoryAttribute("Properties"), DescriptionAttribute("The name of this object.")]
    public string Name { get; set; }
}

internal bool Serialize(DemoClass demo, FileStream fs) {
    XmlSerializer serializer = new XmlSerializer(typeof(DemoClass));
    XmlWriterSettings settings = null;
    XmlWriter writer = null;
    bool result = true;
    try {
        settings = new XmlWriterSettings() {
            Indent = true,
            IndentChars = ("\t"),
            Encoding = Encoding.UTF8,
            NewLineOnAttributes = false,
            NewLineChars = Environment.NewLine,
            NewLineHandling = NewLineHandling.Replace
        };
        writer = XmlWriter.Create(fs, settings);
        serializer.Serialize(writer, demo);
    } catch { result = false; } finally { writer.Close(); }
    return result;
}

据我了解,我可以在其中添加XmlAttribute标记,它将使用该标记作为属性写入文件的所有将来版本;但是,有人告诉我,为了将数据从旧方法转换为新方法,我可能需要某种不确定的“ binder”

任何建议都会对您有所帮助。

注意 :我知道也可以执行以下操作来减小文件大小(降低28%):

Indent = false,
Encoding = Encoding.UTF8,
NewLineOnAttributes = false,

更新 :我目前正尝试在属性上简单地使用XmlAttribute标记,但遇到了一个错误(我所期望的)反序列化失败:

  

出现一个反映类型DemoClass的错误。

更新2 :现在在这里工作了一个新的角度;我决定复制所有需要的类,并使用XmlAttribute标签对其进行更新;然后使用旧类加载旧文件,并使用新类写入新文件。如果这可行,那将是一个很好的解决方法。但是,我敢肯定,没有这种解决方法,就有办法做到这一点。

更新3 更新2(上述)中的方法无法按我预期的方式工作,最终遇到{{3 }} 问题。由于这种方法也涉及很多,因此我最终编写了一个自定义转换方法,该方法使用原始序列化加载XML,然后使用XDocument名称空间中的System.Xml.Linq,创建了一个新的手动XML个文件。最终这是一项耗时的任务,但从长远来看,总体变化较少。它以预期的方式序列化文件(当然,在此处和此处进行一些调整)。既然已经转换了旧文件,下一步就是更新旧的序列化。在此过程中,我已经完成了大约80%的工作,仍然在反射中碰到一些路障:

  

可能没有为原始类型指定XmlAttribute的类型。

在尝试反序列化enum值时会发生这种情况。串行器似乎认为它是一个string值。

1 个答案:

答案 0 :(得分:1)

这是对我有用的代码。

static void Main()
{
    var element = XElement.Load(@"C:\Users\user\Downloads\CollectionOfObjects.xml");
    ElementsToAttributes(element);
    element.Save(@"C:\Users\user\Downloads\CollectionOfObjects-copy.xml");
}

static void ElementsToAttributes(XElement element)
{
    foreach(var el in element.Elements().ToList())
    {
        if(!el.HasAttributes && !el.HasElements)
        {
            var attribute = new XAttribute(el.Name, el.Value);
            element.Add(attribute);
            el.Remove();
        }
        else
            ElementsToAttributes(el);
    }
} 

CollectionOfObjects.xml中的Xml

<CollectionOfObjects>
  <Name>Something</Name>
  <Description>Some description.</Description>
  <Object>
    <Name>Name Of Object</Name>
    <Description>Description of object.</Description>
    <AltName>Alternate name</AltName>
    <ContainerName>Container</ContainerName>
    <Required>true</Required>
    <Length>1</Length>
    <Info>
      <Name>Name</Name>
      <File>Filename</File>
      <Size>20</Size>
      <SizeUnit>MB</SizeUnit>
    </Info>
  </Object>
</CollectionOfObjects>

CollectionOfObjects-copy.xml中的结果Xml

<?xml version="1.0" encoding="utf-8"?>
<CollectionOfObjects Name="Something" Description="Some description.">
  <Object Name="Name Of Object" Description="Description of object." AltName="Alternate name" ContainerName="Container" Required="true" Length="1">
    <Info Name="Name" File="Filename" Size="20" SizeUnit="MB" />
  </Object>
</CollectionOfObjects>