2010-11-24 5 views
2

Ich möchte eine XML-Datendatei (beachten Sie, dass dies kein Schema, .xsd, Datei ist) programmatisch C# verwenden (so ein externer XML-Editor wird nicht funktionieren, es sei denn es hat eine API). Ein Beispiel für die Baumstruktur:Reduzieren einer XML-Datei mit Ref-Attributen

<root> 
    <A> 
     <B att="val"> 
      <C> 
       someData 
      </C> 
     </B> 
    </A> 
    <A> 
     <B> 
      someOtherData 
     </B> 
     <B> 
      moreData 
     </B> 
    </A> 
</root> 

Ich mag es abzuflachen zu:

<root> 
    <A> 
     <B ref="b1" /> 
    </A> 
    <A> 
     <B ref="b2" /> 
     <B ref="b3" /> 
    </A> 
    <B id="b1" att="val"> 
     <C ref="c1" /> 
    </B> 
    <B id="b2"> 
     someOtherData 
    </B> 
    <B id="b3"> 
     moreData 
    </B> 
    <C id="c1"> 
     someData 
    </C> 
</root> 

Gibt es eine Möglichkeit, diese mit C# zu erreichen?

Und gibt es eine Möglichkeit, die flache XML zurück in die Baumstruktur zu verwandeln? Ich möchte etwas so allgemein wie möglich, so dass jede XML-Datei als solche abgeflacht werden könnte.

Es gibt eine similar question on so, aber es behandelt nicht mit refs.

+1

wie ein Sound Guter Anwendungsfall für XSLT. Übrigens nehmen wir an, dass Ihr Beispiel-XML-Dokument kein vollständiges Dokument ist, sondern ein XML-Fragment, da es mehr als ein Element der obersten Ebene enthält. Damit die Ausgabe wohlgeformtes XML ist, müssen Sie ein einzelnes Top-Level-Element haben. Wie möchten Sie mit Attributen und Textknoten umgehen? – LarsH

+0

@LarsH gute Punkte, ich habe die Frage aktualisiert –

+0

@sprocket: Danke für die klärenden Details. Ich arbeite an einer XSLT-Lösung .... sollte ziemlich einfach sein, aber ich habe heute Morgen einige andere dringende Verpflichtungen. Wenn Sie dieses 'xslt' markieren, wetten Sie, dass Sie schnell eine Lösung von jemand anderem haben. Außerdem fehlt Ihrem letzten schließenden Tag "" in Ihrem Eingabe-XML der Schrägstrich. – LarsH

Antwort

3

Diese Transformation:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:variable name="Lower" select= 
    "'abcdefghijklmnopqrstuvwxyz'" 
    /> 

<xsl:variable name="vUpper" select= 
    "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" 
    /> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="/*"> 
    <root> 
    <xsl:apply-templates select="node()"/> 
    <xsl:apply-templates select="/*/*//*" mode="extract"> 
    <xsl:sort select="count(ancestor::*)" data-type="number"/> 
    </xsl:apply-templates> 
    </root> 
</xsl:template> 

<xsl:template match="*[ancestor::*[2]]"> 
    <xsl:variable name="vPos"> 
    <xsl:number level="any"/> 
    </xsl:variable> 

    <xsl:element name="{name()}"> 
    <xsl:attribute name="ref"> 
     <xsl:value-of select= 
     "concat(translate(name(),$vUpper,$Lower),$vPos)"/> 
    </xsl:attribute> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="*" mode="extract"> 
    <xsl:variable name="vPos"> 
    <xsl:number level="any"/> 
    </xsl:variable> 

    <xsl:element name="{name()}"> 
    <xsl:attribute name="id"> 
     <xsl:value-of select= 
     "concat(translate(name(),$vUpper,$Lower),$vPos)"/> 
    </xsl:attribute> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:element> 
</xsl:template> 
</xsl:stylesheet> 

wenn auf dem mitgelieferten XML-Dokument angewandt:

<root> 
    <A> 
     <B att="val"> 
      <C> 
       someData 
      </C> 
     </B> 
    </A> 
    <A> 
     <B> 
      someOtherData 
     </B> 
     <B> 
      moreData 
     </B> 
    </A> 
</root> 

produziert genau das gewünschte, korrekte Ergebnis:

<root> 
    <A> 
     <B ref="b1"/> 
    </A> 
    <A> 
     <B ref="b2"/> 
     <B ref="b3"/> 
    </A> 
    <B id="b1" att="val"> 
     <C ref="c1"/> 
    </B> 
    <B id="b2"> 
      someOtherData 
     </B> 
    <B id="b3"> 
      moreData 
     </B> 
    <C id="c1"> 
       someData 
      </C> 
</root> 

Die Rücktransformation ist:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 
<xsl:key name="kElbyId" match="*" use="@id"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="*[@ref]"> 
    <xsl:apply-templates mode="deepen" 
     select="key('kElbyId',@ref)"/> 
</xsl:template> 

<xsl:template match="*[@id]"/> 
<xsl:template match="*[@id]" mode="deepen"> 
    <xsl:copy> 
    <xsl:apply-templates 
     select="@*[not(name()='id')] | node()"/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

, wenn dieser, umgekehrte Transformation auf dem Ergebnis der Abflachung Transformation oben angewandt wird, das ursprüngliche XML-Dokument hergestellt:

<root> 
    <A> 
     <B att="val"> 
     <C> 
       someData 
      </C> 
     </B> 
    </A> 
    <A> 
     <B> 
      someOtherData 
     </B> 
     <B> 
      moreData 
     </B> 
    </A> 
</root> 
+0

+1 Ich mag dieses neue allgemeine Muster. –

3

Dieses Sheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:variable name="vUppercase" select="'QWERTYUIOPASDFGHJKLZXCVBNM'"/> 
    <xsl:variable name="vLowercase" select="'qwertyuiopasdfghjklzxcvbnm'"/> 
    <xsl:template match="*"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:if test="parent::*/parent::*"> 
       <xsl:attribute name="id"> 
        <xsl:value-of select="translate(name(), 
                $vUppercase, 
                $vLowercase)"/> 
        <xsl:number level="any"/> 
       </xsl:attribute> 
      </xsl:if> 
      <xsl:apply-templates mode="ref"/> 
     </xsl:copy> 
     <xsl:apply-templates/> 
    </xsl:template> 
    <xsl:template match="*" mode="ref"> 
     <xsl:copy> 
      <xsl:attribute name="ref"> 
       <xsl:value-of select="translate(name(), 
               $vUppercase, 
               $vLowercase)"/> 
       <xsl:number level="any"/> 
      </xsl:attribute> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="text()"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Ausgang:

<root> 
    <A> 
     <B ref="b1" /> 
    </A> 
    <B att="val" id="b1"> 
     <C ref="c1" /> 
    </B> 
    <C id="c1"> 
        someData 
    </C> 
    <A> 
     <B ref="b2" /> 
     <B ref="b3" /> 
    </A> 
    <B id="b2"> 
       someOtherData 
    </B> 
    <B id="b3"> 
       moreData 
    </B> 
</root> 

Die Rückseite Sheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="kElementById" match="*[@id]" use="@id"/> 
    <xsl:key name="kElementByRef" match="*[@ref]" use="@ref"/> 
    <xsl:template match="node()|@*" name="identity"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*[key('kElementByRef',@id)]| 
         *[key('kElementByRef',@id)]/@id"/> 
    <xsl:template match="*[@ref]"> 
     <xsl:for-each select="key('kElementById',@ref)"> 
      <xsl:call-template name="identity"/> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

Ausgang:

<root> 
    <A> 
     <B att="val"> 
      <C> 
       someData 
      </C> 
     </B> 
    </A> 
    <A> 
     <B> 
      someOtherData 
     </B> 
     <B> 
      moreData 
     </B> 
    </A> 
</root> 
+0

@Alejandro: Gute Antwort, aber in der umgekehrten Transformation gehen Sie davon aus, dass die einzigen 'ID'-Attribute durch die Abflachungs-Transformation erzeugt würden. Dies ist im Allgemeinen möglicherweise nicht wahr. –

+0

@ Alejandro: Eine weitere kleine Bemerkung ist, dass die Frage nach ids beginnt, die mit Kleinbuchstaben "b" beginnen, nicht mit "B". –

+0

@Dimitre: Ich nahm an, dass die Werte der IDs nicht wirklich wichtig waren, solange sie einzigartig waren. Aber ich könnte mich mit den Anforderungen des OPs irren. – LarsH

2

Sie‘ wahrscheinlich besser dran mit @ Alejandros oder @ Dimitres Stylesheets, aber ich wollte meinen veröffentlichen, seit ich eine funktionierende Version fertig habe:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
     <!-- copy any non-elements --> 
     <xsl:copy-of select="@* | node()[not(self::*)]"/> 
     <!-- transform descendant elements --> 
     <xsl:apply-templates select=".//*" mode="define" /> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*" mode="define"> 
     <xsl:copy> 
     <xsl:attribute name="id"><xsl:value-of select="generate-id()"/></xsl:attribute> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates select="node()" /> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:copy> 
     <xsl:attribute name="ref"><xsl:value-of select="generate-id()"/></xsl:attribute> 
     </xsl:copy> 
    </xsl:template> 

    <!-- Identity transform --> 
    <xsl:template match="@* | node()" mode="ref"> 
     <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Anmerkung: Ich habe nicht versucht

  • die besonderen id Muster, die Sie verwendet zu bewahren. Meine Annahme ist, dass es dir egal ist, was die IDs sind, solange sie einzigartig und stabil sind. Wenn diese Annahme falsch ist, zeigen die vorherigen zwei Antworten, wie die IDs gemäß Ihrem Muster generiert werden.
  • die Reihenfolge, in der Sie die Elementdefinitionen generiert haben, obwohl die Reihenfolge des Originaldokuments von meiner Ausgabe wiederhergestellt werden kann.
  • die Tatsache, dass Ihre Top-Level-Elemente keine ID-Attribute haben. Das wäre ein einfaches Feature, das hinzugefügt werden kann, wie es die anderen Antworten getan haben. Aber hoffentlich ist es nicht notwendig: Top-Level-Elemente sind als solche erkennbar, weil es keine Referenzen darauf gibt.

Wenn mein Sheet auf Ihrem Abtastwerteingang ausgeführt wird, erhalte ich diese Ausgabe (der Abstand ist hässlich, aber ich werde beheben es nicht, da Sie andere gute Antworten haben):

<?xml version="1.0" encoding="utf-8"?> 
<root> 



    <A id="d0e3"> 

     <B ref="d0e5"/> 

    </A> 
    <B id="d0e5" att="val"> 

     <C ref="d0e7"/> 

    </B> 
    <C id="d0e7"> 
      someData 
     </C> 
    <A id="d0e12"> 

     <B ref="d0e14"/> 

     <B ref="d0e17"/> 

    </A> 
    <B id="d0e14"> 
     someOtherData 
     </B> 
    <B id="d0e17"> 
     moreData 
     </B> 
</root> 
+0

+1 Dies ist eine gute allgemeine Antwort auch. –

+0

+1 für eine allgemein gute Antwort. –

Verwandte Themen