Слияние XML-файла со списком изменений - PullRequest
0 голосов
/ 09 ноября 2009

У меня есть два XML-файла, которые сгенерированы другим приложением, которое я не могу контролировать. Первый - это файл настроек, а второй - список изменений, которые следует применить к первому.

Основной файл настроек:

<?xml version="1.0"?>
<preset>
  <var id="9" opt="0" val="6666666"/>
  <var id="9" opt="1" val="10000000"/>
  <var id="9" opt="2" val="10000000"/>
  <var id="9" opt="3" val="10000000"/>
  <var id="9" opt="4" val="0"/>
  <var id="10" opt="0" val="4"/>
  <var id="11" opt="0" val="0"/>
  <var id="15" opt="0" val="75"/>
  <var id="22" opt="0" val="0,0,127,516" type="rect(l,t,r,b)"/>
  <var id="23" opt="0" val="27,18,92,66" type="rect(l,t,r,b)"/>
  <var id="24" opt="0" val="320"/>
  ... Skip 300 lines ...
</preset>

А вот пример изменений:

<?xml version="1.0"?>
<preset>
  <var id="15" opt="0" val="425"/>
  <var id="22" opt="0" val="0,0,127,776" type="rect(l,t,r,b)"/>
  <var id="26" opt="0" val="147"/>
  <var id="27" opt="0" val="147"/>
  <var id="109" opt="1" val="7"/>
  <var id="126" opt="0" val="6,85,85,59" type="crv(t,m,b,vm)"/>
  <var id="157" opt="0" val="1"/>
  ... Skip 10 lines ...
</preset>

Каждая переменная имеет идентификатор и оптимизацию, к которой относится этот идентификатор. По сути, я пытаюсь заменить строки, где id="#" и opt="#" совпадают с версией из файла "change". В приведенном выше примере значение для id="15" opt="0" изменится с 75 на 425.

Был бы какой-нибудь чистый способ сделать это в C #? На первый взгляд, чтение в текстовом виде и пошаговое выполнение изменений с использованием метода поиска-замены кажется наиболее чистым. Подход, рассматривающий это как XmlDocument, кажется гораздо более трудоемким.

Ответы [ 4 ]

3 голосов
/ 09 ноября 2009

Это было бы ужасно неэффективно, если файлы становятся очень большими, но это то, как вы можете сделать это с XmlDocuments:

XmlDocument main = new XmlDocument();
main.Load( "main.xml" );

XmlDocument changes = new XmlDocument();
changes.Load( "changes.xml" );

foreach ( XmlNode mainNode in main.SelectNodes( "preset/var" ) )
{
    string mainId = mainNode.Attributes[ "id" ].Value;
    string mainOpt = mainNode.Attributes[ "opt" ].Value;

    foreach ( XmlNode changeNode in changes.SelectNodes( "preset/var" ) )
    {
        if ( mainId == changeNode.Attributes[ "id" ].Value &&
            mainOpt == changeNode.Attributes[ "opt" ].Value )
        {
            mainNode.Attributes[ "val" ].Value = changeNode.Attributes[ "val" ].Value;
        }
    }
}

// save the updated main document
main.Save( "updated_main.xml" );
2 голосов
/ 10 ноября 2009

Пример выполнения этого в Linq to XML с использованием объединений для связи документов. Сначала я выбираю элементы, соответствующие двум атрибутам, а затем обновляю их до нового значения из файла изменений.

XDocument main = XDocument.Load("XMLFile1.xml");
XDocument changes = XDocument.Load("XMLFile2.xml");

var merge = from entry in main.Descendants("preset").Descendants("var")
            join change in changes.Descendants("preset").Descendants("var")
            on 
               new {a=entry.Attribute("id").Value, b=entry.Attribute("opt").Value}
            equals 
               new {a=change.Attribute("id").Value, b=change.Attribute("opt").Value}
            select new
            {
               Element = entry,
               newValue = change.Attribute("val").Value                       
            };

            merge.ToList().ForEach(i => i.Element.Attribute("val").Value = i.newValue);

            main.Save("XMLFile3.xml");
2 голосов
/ 09 ноября 2009

Не уверен насчет эффективности, но это просто с Linq to XML - последующие действия были немного грубыми - но помня, что замечательный LinqPAD позволит вам запускать программы ... при этом полный кусок кода, который бы работа:

void Main()
{
    XDocument settingsXML = XDocument.Load(@"c:\temp\settings.xml");
    XDocument updateXML = XDocument.Load(@"c:\temp\updates.xml");

    Console.WriteLine("Processing");

    // Loop through the updates
    foreach(XElement update in updateXML.Element("preset").Elements("var"))
    {    
        // Find the element to update    
        XElement settingsElement = 
            (from s in settingsXML.Element("preset").Elements("var")
             where s.Attribute("id").Value == update.Attribute("id").Value &&
                   s.Attribute("opt").Value == update.Attribute("opt").Value
             select s).FirstOrDefault();    
        if (settingsElement != null)    
        {      
            settingsElement.Attribute("val").Value = update.Attribute("val").Value;    
            // Handling for additional attributes here
        }    
        else    
        {   
            // not found handling    
            Console.WriteLine("Not found {0},{1}", update.Attribute("id").Value,
                                                   update.Attribute("opt").Value);
        }
    }
    Console.WriteLine("Saving");
    settingsXML.Save(@"c:\temp\updatedSettings.xml");
    Console.WriteLine("Finis!");
}

Добавление условий использования оставлено в качестве упражнения:)

Есть еще один пример здесь , но он в VB, который имеет больше возможностей с точки зрения XML.

Я также думаю, что можно сделать что-то действительно элегантное с помощью запроса с объединением двух наборов данных XML, генерирующих список динамических типов, содержащих XElement и значение (или значения), которые ему необходимы быть обновленным с. Но я достаточно повеселился (провел достаточно времени) с этим уже на один вечер

0 голосов
/ 09 ноября 2009

XmlDocument был бы идеальным для этого процесса и гораздо менее трудоемким, чем другой метод, который вы предложили. Возможно, вы захотите взглянуть на другие методы, чтобы приблизиться к этому, если использование файлов LARGE Xml в качестве XmlDocument загружает весь документ в память!

В любом случае подход XmlDocument будет выглядеть примерно так:

  1. Загрузите оба файла в соответствующие им объекты XmlDocument.
  2. Выполните итерацию по списку узлов var в файле XmlDocument файла изменений и каждый раз выполняйте запрос xpath для исходного объекта XmlDocument файла, чтобы найти узел с соответствующим идентификатором (используя метод SelectSingleNode () в исходном файле XmlDocument объект), когда найден, отредактируйте атрибут, который вам нужно отредактировать в узле.
  3. Сохранить файл после всех правок.

Я знаю, что я говорю вам, чтобы делать работы, но я не могу объяснить это ясно. Я могу завершить очень грубую версию этой программы менее чем за 30 минут, и у меня есть только около года опыта в c #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...