2 Stimmen

Reduzierung einer Xml-Datei mit ref-Attributen

Ich möchte eine Xml-Datendatei (beachten Sie, dass dies nicht ein Schema, .xsd, Datei) programmatisch mit C# (so ein externer Xml-Editor wird nicht funktionieren, es sei denn, es hat eine API) zu reduzieren. Für ein Beispiel einer Baumstruktur:

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

Ich würde es gerne abflachen:

<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, dies mit C# zu erreichen?

Und gibt es eine Möglichkeit, die flache Xml-Datei wieder in eine Baumstruktur umzuwandeln? Ich möchte etwas so generisch wie möglich, so dass jede Xml-Datei als solche geglättet werden könnte.

Es gibt eine ähnliche Frage zu so aber sie befasst sich nicht mit den Schiedsrichtern.

3voto

Dieses Stylesheet:

<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>

Ausgabe:

<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>

Das umgekehrte Stylesheet:

<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>

Ausgabe:

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

3voto

Dimitre Novatchev Punkte 234995

Diese Umwandlung :

<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>

bei Anwendung auf das bereitgestellte XML-Dokument :

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

produziert genau das gewünschte, richtige 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 umgekehrte Transformation 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 diese umgekehrte Transformation auf das Ergebnis der obigen Glättungstransformation angewendet wird, entsteht das ursprüngliche XML-Dokument :

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

2voto

LarsH Punkte 26458

Du bist wahrscheinlich besser dran, wenn du die Stylesheets von @Alejandro oder @Dimitre verwendest, aber ich wollte meins posten, da ich eine Arbeitsversion fertiggestellt 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

  • die von Ihnen verwendeten id-Muster. Ich gehe davon aus, dass es Ihnen egal ist, wie die IDs lauten, solange sie eindeutig und stabil sind. Wenn diese Annahme falsch ist, zeigen die beiden vorangegangenen Antworten, wie man die IDs nach dem von Ihnen verwendeten Muster erzeugt.
  • die Reihenfolge, in der Sie die Elementdefinitionen erstellt haben, obwohl die Reihenfolge des Originaldokuments aus meiner Ausgabe wiederherstellbar sein sollte.
  • die Tatsache, dass Ihre Top-Level-Elemente keine id-Attribute haben. Das wäre ein einfaches Feature, das man hinzufügen könnte, wie es die anderen Antworten getan haben. Aber hoffentlich ist es nicht notwendig: Elemente der obersten Ebene sind als solche erkennbar, weil es keine Verweise auf sie gibt.

Wenn ich mein Stylesheet auf Ihre Beispieleingabe anwende, erhalte ich diese Ausgabe (die Abstände sind hässlich, aber ich werde sie nicht korrigieren, 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>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X