2016-06-07 7 views
0

Ich entwickle eine App mit C# und versuche, eine XML, die ich von einem JSON bekommen habe, zu vervollständigen. Und damit die XML für meine App gültig ist, muss ich die Elemente mit demselben Namen unter einem Vaterelement gruppieren. Zum Beispiel, ich habe dieses XMLVergleichen Sie Knotennamen in jeder Ebene von XML

<root> 
<row> 
    <id>0001</id> 
    <type>credit</type> 
    <investment>1000</investment> 
    <ppr>0.83</ppr> 
    <candidate> 
     <id>5001</id> 
     <name>Hugo</name> 
    </candidate> 
    <candidate> 
     <id>5002</id> 
     <name>Jack</name> 
    </candidate> 
    <candidate> 
     <id>5005</id> 
     <name>Kate</name> 
    </candidate> 
</row> 

Und ich brauche alle Elemente mit dem Namen Kandidaten-Gruppe, unter einem Vater Knotenkandidaten, wie diese

<root> 
<row> 
    <id>0001</id> 
    <type>credit</type> 
    <investment>1000</investment> 
    <ppr>0.83</ppr> 
<candidates> 
     <candidate> 
       <id>5001</id> 
       <name>Hugo</name> 
     </candidate> 
     <candidate> 
       <id>5002</id> 
       <name>Jack</name> 
     </candidate> 
     <candidate> 
       <id>5005</id> 
       <name>Kate</name> 
     </candidate> 
</candidates> 
</row> 

Aber hier ist mein Problem: Ich kenne die Namen nicht, die ich von der JSON erhalten kann. Also muss ich diesen Vergleich durchführen und das XML vervollständigen, ohne den Namen des "Kandidaten" -Knotens zu kennen. Ich brauche das für jeden Namen, den ich erhalten kann.

Auch in diesem Beispiel hat die XML nur 2 Ebenen, aber es kann eine beliebige Anzahl von Ebenen haben. Ich kann mit dieser Funktion über die XML ohne Problem durchlaufen:

public void findAllNodes(XmlNode node) 
{ 
    Console.WriteLine(node.Name); 
    foreach (XmlNode n in node.ChildNodes) 
    findAllNodes(n); 
} 

Wie kann ich den Vergleich und die Gruppe der Knoten machen?

Antwort

2

Eine relativ naive Implementierung könnte LINQ verwenden, um Elemente nach Namen zu gruppieren und ein übergeordnetes Element für Elemente hinzuzufügen, die mehr als 1 Element in einer Gruppe enthalten. Dies wäre rekursiv, also wurden untergeordnete Elemente eines Elements gruppiert, bis der Baum erschöpft war. Die Naivität ist, dass solch eine Lösung brechen würde, wenn es gemischte Inhaltselemente gäbe, und Elemente gruppieren würde, die keine Geschwister sind (im Grunde führen beide Probleme dazu, dass Dinge in der falschen Reihenfolge enden). Es sollte Ihnen einen guten Start geben und könnte für Ihre Zwecke ausreichen.

private static IEnumerable<XElement> GroupElements(IEnumerable<XElement> elements) 
{ 
    var elementsByName = elements.GroupBy(x => x.Name); 

    foreach (var grouping in elementsByName) 
    { 
     var transformed = grouping.Select(e => 
      new XElement(e.Name, 
       GroupElements(e.Elements()), 
       e.Attributes(), 
       e.Nodes().OfType<XText>())); 

     if (grouping.Count() == 1) 
     { 
      yield return transformed.Single(); 
     } 
     else 
     { 
      var groupName = grouping.Key + "s"; 
      yield return new XElement(groupName, transformed); 
     } 
    } 
} 

diese Weise können Sie durch Parsen/Laden Sie Ihre vorhandenen XML und dann die Elemente Wurzel Transformation und ein neues Dokument von denen zu erstellen:

var original = XDocument.Parse(xml); 
var grouped = new XDocument(GroupElements(original.Elements())); 

Siehe this fiddle für eine funktionierende Demo.

+0

Vielen Dank! es ist genau das, was ich suchte – FRassetto

+0

Es hat auch für mich funktioniert! Vielen Dank –

0

Hier ist ein XSLT-2.0-Lösung:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes"/> 

    <xsl:template match="*[*]"> 
     <xsl:copy> 
      <xsl:for-each-group select="*" group-adjacent="node-name(.)"> 
       <xsl:choose> 
        <xsl:when test="count(current-group()) > 1"> 
         <xsl:element name="{name()}s" namespace="{namespace-uri()}"> 
          <xsl:apply-templates select="current-group()"/> 
         </xsl:element> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:apply-templates select="current-group()"/> 
        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:for-each-group> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*"> 
     <xsl:copy-of select="."/> 
    </xsl:template> 
</xsl:stylesheet> 

Ausgang:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <row> 
     <id>0001</id> 
     <type>credit</type> 
     <investment>1000</investment> 
     <ppr>0.83</ppr> 
     <candidates> 
     <candidate> 
      <id>5001</id> 
      <name>Hugo</name> 
     </candidate> 
     <candidate> 
      <id>5002</id> 
      <name>Jack</name> 
     </candidate> 
     <candidate> 
      <id>5005</id> 
      <name>Kate</name> 
     </candidate> 
     </candidates> 
    </row> 
</root> 

Einschränkungen

  • Es ist nicht gemischte Inhalte (Elemente mit Kindern umgehen kann und Text Inhalt)

  • Es fällt Attribute (leicht behoben)

Verwandte Themen