Как переместить дочерние элементы в атрибуты родительских элементов (XML)
У меня сейчас есть XML
файл довольно большой по размеру (примерно 800MB
). Я пробовал несколько попыток ( здесь речь идет о сжатии), чтобы работать с ним в его текущем состоянии; однако, они не были очень успешными, поскольку занимают довольно много времени.
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 минуты, чтобы даже открыть. Небеса помогут вам, если вы попытаетесь найти файл, потому что это занимает час или дольше.
Вы можете видеть, как это проблематично. Я сделал тест на разницу в размерах (очевидно, не с этим файлом), но с демонстрационным файлом. Я создал файл и преобразовал ненужные дочерние узлы в атрибуты, и это уменьшило размер демонстрационных файлов на 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
добавьте к нему тег, и он запишет все будущие версии файла с этим тегом в качестве атрибутов; однако мне сказали, что для преобразования данных из старого способа в новый мне может понадобиться некая "связка", в которой я не уверен.
Любые рекомендации будут полезны здесь.
ПРИМЕЧАНИЕ: я знаю, что можно сделать следующее, чтобы уменьшить размер файла (упал на 28%):
Indent = false,
Encoding = Encoding.UTF8,
NewLineOnAttributes = false,
Обновление: в настоящее время я пытаюсь просто использовать XmlAttribute
тег на свойства, и я столкнулся с ошибкой (что я ожидал), где отражение не удалось при десериализации:
Произошла ошибка, отражающая тип
DemoClass
,
Обновление 2: теперь работает новый угол здесь; Я решил скопировать все необходимые классы, обновить их с помощью XmlAttribute
тег; затем загрузите старый файл со старыми классами и запишите новый файл с новыми классами. Если это сработает, то это будет отличное решение. Тем не менее, я уверен, что есть способ сделать это без этого обходного пути.
Обновление 3: метод в обновлении 2 (выше) не работает так, как я ожидал, и я столкнулся с этой проблемой. Поскольку этот подход также активно используется, я в итоге написал собственный метод преобразования, который использовал оригинальную сериализацию для загрузки XML
затем с помощью XDocument
от System.Xml.Linq
пространство имен, я создал новый XML
документ от руки. В конечном итоге это заняло много времени, но в конечном итоге изменилось меньше. Он сериализует файл ожидаемым образом (с некоторыми изменениями здесь и там, конечно). Следующим шагом было обновление старой сериализации теперь, когда старые файлы были конвертированы. Я прошел примерно 80% этого процесса, все еще сталкиваясь с некоторыми дорожными неровностями тут и там с отражением:
Тип для XmlAttribute не может быть указан для примитивных типов.
Это происходит при попытке десериализации enum
значение. Сериализатор, кажется, считает, что это string
значение вместо
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);
}
}
XML в CollectionOfObjects.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>
Результат Xml в CollectionOfObjects-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>