2009-11-09 3 views
0

У меня есть два файла XML, которые генерируются другим приложением, для которого я не контролирую. Первый - это файл настроек, а второй - список изменений, которые должны быть применены к первому.Слияние 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 относится. В принципе, я ищу, чтобы заменить строки, где id="#" и opt="#" совпадают с версией из файла изменений. В приведенном выше примере значение для id="15" opt="0" изменилось бы с 75 до 425.

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

+0

Это одноразовая вещь или что-то, что вам придется делать на регулярной основе? – Josh

+0

Регулярная основа. В основном пользователь сохранит изменения, которые SDK автоматически генерирует, и я объединю их с исходными настройками. –

ответ

3

Это было бы очень неэффективно, если файлы становятся очень большими, но это, как вы можете сделать это с 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"); 
+1

Это прекрасно работало как есть. В качестве консольного приложения общее время выполнения составляло 21 мс для 584-строчного XML-файла с 20 изменениями, что отлично подходит для его использования. Благодаря! –

0

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

В любом случае подход XmlDocument будет что-то вроде:

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

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

2

Не уверен в эффективности, но это прямолинейно с Linq для 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!"); 
} 

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

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

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

2

Пример выполнения этого в Linq для 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"); 
+0

Мне было бы интересно узнать, как это решение сравнивается с производительностью с принятым ответом на грубую силу выше. –

Смежные вопросы