2013-10-19 4 views
4

여기 까다로운 질문이 있습니다. 두 개의 xml 파일 비교 누락 된 요소 추가

나는 다음과 같습니다 하나 개의 파일, MainFile.XML 가지고 :

<xml> 
    <header> 
    <some></some> 
    <addThis></addThis> 
    </header> 
    <footer></footer> 
    <this> 
    <is> 
     <deep> 
     <like></like> 
    </deep> 
    </is> 
    </this> 
<test></test> 
<page></page> 
<addThis></addThis> 

을 그리고 내 다른 파일 LangFile.XML은 다음과 같습니다.

<xml> 
    <header> 
    <some>English file</some> 
    </header> 
    <footer>Footer</footer> 
    <this> 
    <is> 
     <deep> 
     <like>Hey</like> 
     </deep> 
    </is> 
    </this> 
    <test>Something</test> 
</xml> 

나는 내 MainFile.XML 일치하도록 내 LangFile.XML을 업데이트 할하지만 난 LangFile의 모든 텍스트 값을 유지해야합니다.

나는 LangFile는 업데이트 후 다음과 같이 할 : 예상 출력

<xml> 
    <header> 
    <some>English file</some> 
    <addThis></addThis> 
    </header> 
    <footer>Footer</footer> 
    <this> 
    <is> 
    <deep> 
     <like>Hey</like> 
    </deep> 
    </is> 
    </this> 
    <test>Something</test> 
    <page></page> 
    <addThis></addThis> 
</xml> 

나는이 대답을 검토 한하지만 파일을 업데이트하고 값을 유지할 필요가 ... Compare two text files line by line

까다로운 부분은 중첩, 그것은

내 문제는 내가 거라고 있다는 것입니다 ... 깊은 X 레벨 1 개 수준까지 사이에 무엇이든 될 수있다 ont 트리에서 더 깊게 진행될 때 행 단위로 행을 비교하는 방법을 알고 있습니다. 그러나 이와 같은 시도를 했었지만 im이 붙어 있습니다 ... 새 목록에 특정 자손을 추가하는 방법을 모릅니다.

String directory = @"C:\Utv\XmlTest"; 

var mainFile = XDocument.Load(Path.Combine(directory, "MainFile.XML")); 
var langFile = XDocument.Load(Path.Combine(directory, "LangFile.XML")); 

//Get all descendant nodes 
var mainFileDesc = mainFile.Root.Descendants().ToList(); 
var langFileDesc = langFile.Root.Descendants().ToList(); 

//Loop through the mainfile 
for(var i = 0; i < mainFileDesc.Count(); i++) 
{ 
    var mainRow = mainFileDesc[i]; 
    var langRow = langFileDesc[i]; 

    //Compare the rows descendants, if not the same, add the mainRow to the langRow 
    if(mainRow.Descendants().Count() != langRow.Descendants().Count()) 
    { 
     //Here I want to check if the mainRow != the langRow 
        //if not, add the mainRow to the langFile list 
        if(mainRow != langRow) 
        { 
         langFileDesc.Insert(i, mainRow); 
        } 
    } 
} 

임 이제 다음 오류가 : 나는 목록에 추가해야하는 이유

목록이 동일한 길이를 가지고 있지 않기 때문이다
var langRow = langFileDesc[i]; 
Message Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index 

, 그건 ...

+0

몇 개의 아이템이있을 수 있습니까? 얼마나 중첩 되니? 모든 ID 값 은요? 2 개의 텍스트 값이 다른 경우 어떻게됩니까? –

+0

주 파일에는 절대로 텍스트 값이 없습니다. 중첩은 융통성이 있으며, 1 -1000 레벨 사이의 어떤 것도 될 수 있습니다 ... – JOSEFtw

+0

1000 레벨? XML을 사용해서는 안되며 여기서 재귀적인 해결책을 찾고있을 것입니다. –

답변

2

내가 이해하기에, 당신이하고 싶은 것은 두 파일이 비슷한 구조를 가지고 있다는 것을 고려하여 xml 파일을 다른 xml 파일로 업데이트하는 것이다. 그렇다면, 당신이 XPath는 함께이 일을 시도 할 수 있습니다,이 것 같은 뭔가 :

private static void Main(string[] args) 
    { 
     try 
     { 
      XmlDocument xml1 = new XmlDocument(); 
      xml1.Load(@"C:\testxml\MainFile.xml"); 
      XPathNavigator nav = xml1.CreateNavigator(); 

      //Create the langFile Navigator 
      XPathDocument xml2 = new XPathDocument(@"C:\testxml\LangFile.xml"); 
      XPathNavigator nav2 = xml2.CreateNavigator(); 
      //Select all text nodes. 
      var nodes = nav2.SelectDescendants(XPathNodeType.Text, true); 
      while (nodes.MoveNext()) 
      { 
       //Update the MainFile with the values in the LangFile 

       var c = nav.SelectSingleNode(GetPath(nodes.Clone().Current));//(1*) 
       if(c != null) 
       { 
        c.MoveToFirstChild(); 
        c.SetValue(nodes.Current.Value); 
       } 
      } 
      Console.WriteLine(xml1.InnerXml); 
      Console.ReadKey(); 
     } 
     catch 
     { 
     } 
    } 
    private static string GetPath(XPathNavigator navigator) 
    { 
     string aux =string.Empty; 
     while (navigator.MoveToParent()) 
     { 
      aux = navigator.Name + "/"+aux; 
     } 
     return "/" + (aux.EndsWith("/")?aux.Remove(aux.LastIndexOf('/')):aux); 
    } 
당신이 당신의 파일이 중첩 얼마나 알 필요가 없습니다이와

하지만, XML은 두 개 이상의 노드가있는 경우 같은 레벨에 같은 이름으로, (* 1) 주석이있는 줄에서 그것을 관리해야합니다. 도움이되기를 바랍니다.

+0

예 다음 작업을 수행하려고합니다. 하나의 MainFile.XML을 가지고이 파일에 수동으로 행을 추가 할 수 있습니다. 그런 다음 LangFile.XML과 MainFile을 비교하고 Langfile에 mainfile의 행이 없다면 추가하려고합니다. 더 많은 시간이있을 때 코드를 살펴 보겠습니다.하지만보기에는 좋습니다! – JOSEFtw

+0

매력처럼 작동합니다! 감사합니다 – JOSEFtw

+1

당신을 환영합니다! :) –

2

이 작업을 재귀 적으로 수행하는 것이 좋습니다. 나는 단순한 파일 복사본이 당신이 원하는 것이 아닌 것으로 추정하고 있습니다.

이 글을 쓰는 동안 다른 하나를 대답으로 확인했습니다. 나는 그것이 당신을 위해 일하기를 희망하지만, 여기에 또 다른 접근법이 있습니다. 내 접근 방식이 더 복잡해 보입니다. Mariano의 작품이 훌륭하다면.

/// <summary> 
/// Copy A to B where B doesn't have A nodes. 
/// </summary> 
public static void EvenUp(XElement A, XElement B) 
{ 
    XNode lastB = null, nodeA = null, nodeB = null; 

    Action Copy_A_To_B =() => 
    { 
     if (null == lastB) 
      B.AddFirst(nodeA); 
     else 
      lastB.AddAfterSelf(nodeA); 
    }; 

    var listA = A.Nodes().ToList(); 
    var listB = B.Nodes().ToList(); 
    int a, b; 

    for (a = 0, b = 0; a < listA.Count && b < listB.Count; a++, b++) 
    { 
     nodeA = listA[a]; 
     nodeB = listB[b]; 

     XElement xA = nodeA as XElement, 
      xB = nodeB as XElement; 

     XText tA = nodeA as XText, 
      tB = nodeB as XText; 

     if (null != xA && null != xB) 
     { 
      if (xA.Name.LocalName == xB.Name.LocalName) 
       EvenUp(xA, xB); 
      else 
      { 
       Copy_A_To_B(); 
       EvenUp(A, B); // Restart this iteration for various reasons such as 
           // the next nodeA might be the same as current nodeB 
       return; 
      } 
     } 
     else if (null != xA) 
      Copy_A_To_B(); 
     else if (null != tA && null != tB) 
     { 
      if (tA.Value != tB.Value) 
       tB.Value = tA.Value; 
     } 
     else if (null != tA) 
      Copy_A_To_B(); 

     lastB = nodeB; 
    } 
    for (; a < listA.Count; a++) 
    { 
     nodeA = listA[a]; 
     Copy_A_To_B(); 
     if (null == lastB) 
      lastB = B.FirstNode; 
     else 
      lastB = lastB.NextNode; 
    } 
} 

당신이 그것을 테스트 할 수

XElement mainFile = XElement.Load("xmlfile1.xml"); 
XElement langFile = XElement.Load("xmlfile2.xml"); 

EvenUp(mainFile, langFile); 

Console.WriteLine(langFile.ToString()); 
Console.ReadLine(); 

그런 다음 다른 방향을 복사 할 경우, 단순히 다시 있지만 인수로 전화를 전환 : EvenUp(langFile, mainFile) 다음 두 파일은 중복해야합니다.

+0

Im은 솔루션을 시도 할 것입니다. 처음에는 재귀 적 솔루션에 대해 생각하고 있었지만 처음에는 깨끗한 해결책으로 보였습니다. 내일 시험 할 것이고 답장을 보내 주셔서 감사합니다. :) – JOSEFtw

+0

이것은 재사용 할 수있는 기능이기 때문에 더 나은 대답입니다. – jjthebig1