1

我有一个具有多个<Page Pageid="1">节点的 XML。所有这些节点<Para Paraid="1">下都有节点。我想做单次出现节点,以便属于同一节点的<Page>所有节点都显示为特定页面的子节点。例如<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

如果您使用的是 .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 个组,其中一组包含两个PageID="1"元素。

在第 3 行,我们遍历这 3 个组并仅提取第一个 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 回答
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 回答