2012-02-13 156 views
1

我有一個Sql表中的Xml列。所有的Xml文件都有相同的模式,我想將這些Xml的一部分合並在一起。什麼是合併多個Xml的最佳解決方案

例如,對於X1:

<A> 
    <B> 
     <C id='101'> 
      <D id='102'>abcd</D> 
     </C> 
     <C id='103'> 
      <D id='104'>zxcv</D> 
     </C> 
    </B> 
</A> 

和X2:

<A> 
    <B> 
     <C id='101'> 
      <D id='102'>abcd</D> 
      <D id='501'>abef</D> 
     </C> 
     <C id='502'> 
      <D id='503'>efgh</D> 
     </C> 
    </B> 
</A> 

X1 + X2 = ...

<A> 
    <B> 
     <C id='101'> 
      <D id='102'>abcd</D> 
      <D id='501'>abef</D> 
     </C> 
     <C id='103'> 
      <D id='104'>zxcv</D> 
     </C>    
     <C id='502'> 
      <D id='503'>efgh</D> 
     </C> 
    </B> 
</A> 

所以其選擇是最佳的,以及如何:

  • 的XQuery在SQL
  • C#的XDocument和XPath
  • ...
+0

你想要通過ID連接的元素的聯合? – Jodrell 2012-02-13 17:38:10

+0

@Jodrell。 – ARZ 2012-02-13 17:40:02

+1

@ARZ,如果X2中的''的值爲'12345',可能出現這種情況嗎?如果是,如何合併這些文件? – 2012-02-13 17:44:41

回答

2

我認爲解決這個最好的方法是編寫合併使用個類,與我們的區別總是從第一個文檔中訪問節點,並與第二個文檔中的節點並行訪問。

整體設計會是這樣的:

class XmlMerger 
{ 
    public XDocument Merge(XDocument first, XDocument second); 

    private XElement MergeElements(XElement first, XElement second); 

    private XAttribute MergeAttributes(XAttribute first, XAttribute second); 

    private XText MergeTexts(XText first, XText second); 
} 

具體實施看起來是這樣的:

class XmlMerger 
{ 
    public XDocument Merge(XDocument first, XDocument second) 
    { 
     return new XDocument(MergeElements(first.Root, second.Root)); 
    } 

    private XElement MergeElements(XElement first, XElement second) 
    { 
     if (first == null) 
      return second; 

     if (second == null) 
      return first; 

     if (first.Name != second.Name) 
      throw new InvalidOperationException(); 

     var firstId = (string)first.Attribute("id"); 
     var secondId = (string)second.Attribute("id"); 

     // different ids 
     if (firstId != secondId) 
      throw new InvalidOperationException(); 

     var result = new XElement(first.Name); 

     var attributeNames = first.Attributes() 
      .Concat(second.Attributes()) 
      .Select(a => a.Name) 
      .Distinct(); 

     foreach (var attributeName in attributeNames) 
      result.Add(
       MergeAttributes(
        first.Attribute(attributeName), 
        second.Attribute(attributeName))); 

     // text-only elements 
     if (first.Nodes().OfType<XText>().Any() || 
      second.Nodes().OfType<XText>().Any()) 
     { 
      var firstText = first.Nodes().OfType<XText>().FirstOrDefault(); 
      var secondText = second.Nodes().OfType<XText>().FirstOrDefault(); 

      // we're not handling mixed elements 
      if (first.Nodes().Any(n => n != firstText) || 
       second.Nodes().Any(n => n != secondText)) 
       throw new InvalidOperationException(); 

      result.Add(MergeTexts(firstText, secondText)); 
     } 
     else 
     { 
      var elementNames = first.Elements() 
       .Concat(second.Elements()) 
       .Select(e => e.Name) 
       .Distinct(); 

      foreach (var elementName in elementNames) 
      { 
       var ids = first.Elements(elementName) 
        .Concat(second.Elements(elementName)) 
        .Select(e => (string)e.Attribute("id")) 
        .Distinct(); 

       foreach (var id in ids) 
       { 
        XElement firstElement = first.Elements(elementName) 
         .SingleOrDefault(e => (string)e.Attribute("id") == id); 
        XElement secondElement = second.Elements(elementName) 
         .SingleOrDefault(e => (string)e.Attribute("id") == id); 

        result.Add(MergeElements(firstElement, secondElement)); 
       } 
      } 
     } 

     return result; 
    } 

    private XAttribute MergeAttributes(XAttribute first, XAttribute second) 
    { 
     if (first == null) 
      return second; 

     if (second == null) 
      return first; 

     if (first.Name != second.Name) 
      throw new InvalidOperationException(); 

     if (first.Value == second.Value) 
      return new XAttribute(first); 

     // can't merge attributes with different values 
     throw new InvalidOperationException(); 
    } 

    private XText MergeTexts(XText first, XText second) 
    { 
     if (first == null) 
      return second; 

     if (second == null) 
      return first; 

     if (first.Value == second.Value) 
      return new XText(first); 

     // can't merge texts with different values 
     throw new InvalidOperationException(); 
    } 
} 

如果這個代碼遇到一些它不能處理(例如節點具有相同ID但不同的文本;或評論),它會引發異常。

+0

謝謝,但這種方法僅適用於兩個XDoc,並且必須擴展才能合併多個XDoc。 – ARZ 2012-02-14 07:31:29

+1

如果你可以合併2,那麼你可以合併它們全部 – Jodrell 2012-02-14 08:28:55

+0

@ARZ,Jodrell是對的。只需合併文件A和B,然後將結果與文件C等合併。 – svick 2012-02-14 12:07:50

1

我會在XQuery中做到這一點。它少得多的代碼。下面的例子是用純XQuery 1.0完成的。使用XQuery 3.0(因爲它支持group by)或使用XQuery Scripting會更容易。

declare variable $sequence := (
    <A> 
     <B> 
      <C id='101'> 
       <D id='102'>abcd</D> 
      </C> 
      <C id='103'> 
       <D id='104'>zxcv</D> 
      </C> 
     </B> 
    </A> 
    , 
    <A> 
     <B> 
      <C id='101'> 
       <D id='102'>abcd</D> 
       <D id='501'>abef</D> 
      </C> 
      <C id='502'> 
       <D id='503'>efgh</D> 
      </C> 
     </B> 
    </A> 
); 

declare function local:merge($dsequence) { 
    let $dfirst := $dsequence[1] 
    let $dextended := <D cid="{$dfirst/../@id}" id="{$dfirst/@id}">{$dfirst/text()}</D> 
    return 
    if (count($dsequence) eq 1) then 
     (: nothing to merge :) 
     $dextended 
    else 
     (: merging :) 
     let $tomerge := local:merge(fn:subsequence($dsequence, 2)) 
     return 
     if ($tomerge[@cid eq $dextended/@cid] and $tomerge[@id eq $dextended/id]) then 
      $tomerge 
     else 
      ($tomerge, $dextended) 
}; 

<A><B> { 
    let $merged := local:merge($sequence/B/C/D) 
    let $ckeys := fn:distinct-values(fn:data($merged/@cid)) 
    for $ckey in $ckeys 
    return 
    <C id="{$ckey}"> { 
    for $dkey in fn:distinct-values(data($merged[@cid eq $ckey]/@id)) 
    let $d := ($merged[@cid eq $ckey and @id eq $dkey])[1] 
    return <D id="{$d/@id}">{$d/text()}</D> 
    }</C> 
} 
</B></A> 
+0

對不起,但如何在sql表上運行它? ;) – ARZ 2012-02-15 10:38:07

+0

我對SQL數據庫中的XQuery不是很熟悉。如何從XQuery中訪問XML表?它是否必須從PL/SQL傳遞?它是如何工作的? – 2012-02-16 09:12:58

相關問題