如何将子元素移动到父元素的属性 (XML)
How to Move Child Elements to Attributes of Parent Elements (XML)
我目前有一个 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>
每个object下都有相当大的数据块,其中很多子节点都可以做成parents上的属性:
<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 分钟才能打开。如果您尝试搜索该文件,上帝会帮助您,因为它需要一个小时或更长时间。
你可以看出这是多么有问题。我已经对大小差异进行了测试(显然不是使用此文件),而是使用了一个演示文件。我创建了一个文件并将不必要的子节点转换为属性,它使演示文件的大小减少了 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,
Update:我目前正尝试简单地在属性上使用 XmlAttribute
标签,但我遇到了错误(这是我所期望的)反序列化时反射失败的地方:
There was an error reflecting type DemoClass
.
更新 2:现在在这里工作一个新的角度;我决定复制所有需要的 类,用 XmlAttribute
标签更新它们;然后用旧 类 加载旧文件并用新 类 写入新文件。如果这有效,那么它将是一个很好的解决方法。但是,我确信有一种方法可以在没有这种解决方法的情况下做到这一点。
更新 3:更新 2(上) 中的方法无效我期望的方式,我最终遇到了 this 问题。由于这种方法也涉及很多,所以我最终编写了一个自定义转换方法,该方法使用原始序列化来加载 XML
,然后使用 System.Xml.Linq
命名空间中的 XDocument
,我创建了一个新的XML
手工记录。这最终成为一项耗时的任务,但在漫长的 运行 中整体变化较小。它以预期的方式序列化文件(当然在这里和那里进行一些调整)。既然旧文件已经转换,下一步就是更新旧的序列化。我已经完成了大约 80% 的过程,但仍然在这里和那里遇到一些路障:
The type for XmlAttribute may not be specified for primitive types.
尝试反序列化 enum
值时会发生这种情况。序列化程序似乎认为它是一个 string
值。
这是对我有用的代码。
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中的结果Xml-copy.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>
我目前有一个 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>
每个object下都有相当大的数据块,其中很多子节点都可以做成parents上的属性:
<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 分钟才能打开。如果您尝试搜索该文件,上帝会帮助您,因为它需要一个小时或更长时间。
你可以看出这是多么有问题。我已经对大小差异进行了测试(显然不是使用此文件),而是使用了一个演示文件。我创建了一个文件并将不必要的子节点转换为属性,它使演示文件的大小减少了 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,
Update:我目前正尝试简单地在属性上使用 XmlAttribute
标签,但我遇到了错误(这是我所期望的)反序列化时反射失败的地方:
There was an error reflecting type
DemoClass
.
更新 2:现在在这里工作一个新的角度;我决定复制所有需要的 类,用 XmlAttribute
标签更新它们;然后用旧 类 加载旧文件并用新 类 写入新文件。如果这有效,那么它将是一个很好的解决方法。但是,我确信有一种方法可以在没有这种解决方法的情况下做到这一点。
更新 3:更新 2(上) 中的方法无效我期望的方式,我最终遇到了 this 问题。由于这种方法也涉及很多,所以我最终编写了一个自定义转换方法,该方法使用原始序列化来加载 XML
,然后使用 System.Xml.Linq
命名空间中的 XDocument
,我创建了一个新的XML
手工记录。这最终成为一项耗时的任务,但在漫长的 运行 中整体变化较小。它以预期的方式序列化文件(当然在这里和那里进行一些调整)。既然旧文件已经转换,下一步就是更新旧的序列化。我已经完成了大约 80% 的过程,但仍然在这里和那里遇到一些路障:
The type for XmlAttribute may not be specified for primitive types.
尝试反序列化 enum
值时会发生这种情况。序列化程序似乎认为它是一个 string
值。
这是对我有用的代码。
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中的结果Xml-copy.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>