Как переместить дочерние элементы в атрибуты родительских элементов (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>

Другие вопросы по тегам