1

<Page Pageid="1">複数のノードを持つ XML があります。そのようなすべてのノードには<Para Paraid="1">、その下にノードがあります。同じノードに属する<Page>すべてのノードが特定のページの子として表示されるように、ノードを 1 回出現させたいと考えています。例えば<Para><Page>

入力:

<Page PageID="**1**">
   <Para ParaID="1">
     <some nodes as child of para>
   </Para>
</Page>
<Page PageID="**2**">
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
</Page>
<Page PageID="**1**"> <!Page 1 encountered again>
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
</Page>
<Page PageID="**3**">
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
</Page>

期待される出力:

<Page PageID="**1**">
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
   <Para ParaID="**2**">           <!all <Para> of Page 1 are under single <Page> node>
     <some nodes as child of para>
   </Para>
</Page>
<Page PageID="**2**">
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
</Page>
<Page PageID="**3**">
   <Para ParaID="**1**">
     <some nodes as child of para>
   </Para>
</Page>
4

2 に答える 2

0

これは特に効率的ではありません - を使用するより高速な方法がありますxsl:key- しかし、ソース ドキュメントが不当に大きくないほとんどの場合に機能します。以下を恒等変換に追加します。

<!-- filter out Page elements that aren't the first occurrence for their PageID -->
<xsl:template match="Page[@PageID = preceding-sibling::Page/@PageID]"/>

<!-- for each distinct page, copy all Page child nodes with the current PageID -->      
<xsl:template match="Page">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="/root/Page[@PageID = current()/@PageID]/node()"/>
  </xsl:copy>
</xsl:template>

Pageグループ化しようとしている要素に属性があり、上記は基本的にそれらを無視する場合に何をすべきかを述べていないことに注意してください。Page指定された を持つ最初の要素から属性のみをコピーしますPageID

于 2011-03-10T17:32:16.443 に答える
0

.NET 3.5 を使用している場合は、XDocument ファミリと Linq 拡張機能を使用して、タスクをかなり軽くすることができます。

var doc1 = XDocument.Parse(stringContainingYourXML);
var groups = doc1.Root.Elements().ToLookup(elt => elt.Attribute("PageID").Value);
var unique = groups.AsEnumerable().Select(group => group.First());
var doc2 = new XDocument(new XElement("root", unique));

これの説明は、2 行目で同じ値を含む要素PageIDがグループ化されたルックアップ テーブルを作成していることです。XML の例を考えると、4 つ<Page/>の要素を取り、3 つのグループを作成し、1 つのグループに両方のPageID="1"要素を含めます。

3 行目では、3 つのグループをループし、1 つの最初の XML 要素だけを抽出します。4 行目では、これらの 3 つの要素を新しいドキュメントに詰め込みます。結果の XML は次のとおりです。

<root>
  <Page PageID="**1**">
    <Para ParaID="1" />
  </Page>
  <Page PageID="**2**">
    <Para ParaID="**1**" />
  </Page>
  <Page PageID="**3**">
    <Para ParaID="**1**" />
  </Page>
</root>

更新:2011/03/12

以下のコードは、ページの重複インスタンスからの段落を自動インクリメント方式でマージする必要があることを考慮しています。

改訂されたソリューションは、以前のソリューションと比較してかなりひどいものですが、ParaID 値 (特にその形式) をいじるのは非常に面倒でした。私はこれを誇りに思っていませんが、ここにあります:

using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace SO {
    class Program {
        static void Main(string[] args) {
            var doc1 = XDocument.Parse(xmlstr);
            var groups = doc1.Root.Elements().ToLookup(page => page.Attribute("PageID").Value);
            var doc2 = new XDocument(new XElement("root"));

            foreach (var group in groups) {
                var firstpage = group.First();
                var startindex = firstpage.Elements("Para").Last().Attribute("ParaID").Value;
                var lastindex = int.Parse(Regex.Match(startindex, @"\d+").Value);

                // Duplicate pages...
                firstpage.Add(
                    group.Skip(1)
                         .SelectMany(page => page.Elements("Para"))
                         .Select(
                             para => {
                                 para.Attribute("ParaID").Value = Regex.Replace(
                                     para.Attribute("ParaID").Value,
                                     @"\d+",
                                     m => (++lastindex).ToString()
                                 );
                                 return para;
                             }
                         )
                );

                doc2.Root.Add(firstpage);
            }

            Console.WriteLine(doc2);
            Console.ReadKey(true);
        }
    }
}
于 2011-03-10T13:27:09.637 に答える