Объединение нескольких существующих узлов в одном родительском узле с использованием C # - PullRequest
1 голос
/ 10 марта 2011

У меня есть XML, имеющий несколько <Page Pageid="1"> узлов. Все такие узлы имеют <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>

Ответы [ 2 ]

0 голосов
/ 10 марта 2011

Это не особенно эффективно - есть более быстрый метод, который использует 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.

0 голосов
/ 10 марта 2011

Если вы используете .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);
        }
    }
}
...