Can you transform unordered xml to match an xsd:sequence order?

后端 未结 2 783
灰色年华
灰色年华 2020-12-18 11:50

Hi i need to transform unorderd xml using xslt to the correct order as specified in an xsd schema


    

        
相关标签:
2条回答
  • 2020-12-18 12:10

    Here is an XSLT 1.0 solution:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:key name="kxsElemByName" match="xs:element" use="@name"/>
    
     <xsl:variable name="vSchema" select=
      "document('file:///c:/temp/delete/schema.xsd')"/>
    
     <xsl:variable name="vDoc" select="/"/>
    
     <xsl:template match="/*">
      <xsl:variable name="vElem" select="."/>
    
      <xsl:for-each select="$vSchema">
       <xsl:apply-templates select=
         "key('kxsElemByName', name($vElem))">
        <xsl:with-param name="pElement" select="$vElem"/>
       </xsl:apply-templates>
      </xsl:for-each>
     </xsl:template>
    
     <xsl:template match="xs:element">
      <xsl:param name="pElement"/>
    
      <xsl:element name="{name($pElement)}">
       <xsl:apply-templates mode="generate"
            select="xs:complexType/xs:sequence/*">
         <xsl:with-param name="pParent" select="$pElement"/>
       </xsl:apply-templates>
      </xsl:element>
     </xsl:template>
    
     <xsl:template match="xs:element" mode="generate">
      <xsl:param name="pParent"/>
      <xsl:variable name="vProp" select=
       "$pParent/property[@name = current()/@name]"/>
    
      <xsl:element name="{$vProp/@name}">
       <xsl:value-of select="$vProp/@value"/>
      </xsl:element>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the provided XML document (Person renamed to person to match the schema):

    <person>
        <property name="address" value="5" />
        <property name="firstname" value="1234567890" />
        <property name="lastname" value="The BFG" />
    </person>
    

    and if the provided XML schema is in the file c:\temp\delete\schema.xsd:

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
      <xs:element name="person">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="firstname" type="xs:string"/>
            <xs:element name="lastname" type="xs:string"/>
            <xs:element name="address" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    

    then the wanted, correct result is produced:

    <person>
       <firstname>1234567890</firstname>
       <lastname>The BFG</lastname>
       <address>5</address>
    </person>
    
    0 讨论(0)
  • 2020-12-18 12:29

    This may not be the best way, but it seems to work ok. I'm not sure if the order that the xs:element's are processed is guaranteed though. Also, this is an XSLT 2.0 answer tested with Saxon-HE 9.3.0.5 in oXygen.

    XML Input (modified the case of Person to match the schema):

    <person>
      <property name="address" value="5" />
      <property name="firstname" value="1234567890" />
      <property name="lastname" value="The BFG" />
    </person>
    

    External XSD Schema file (schema.xsd):

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
      <xs:element name="person">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="firstname" type="xs:string"/>
            <xs:element name="lastname" type="xs:string"/>
            <xs:element name="address" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    

    XSLT 2.0 Stylesheet:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all">
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
    
      <xsl:variable name="input">
        <xsl:copy-of select="/"/>
      </xsl:variable>
    
      <xsl:template match="/*">
        <xsl:variable name="firstContext" select="name()"/>
        <xsl:variable name="xsdElems" select="document('schema.xsd')/xs:schema/xs:element[@name=$firstContext]/xs:complexType/xs:sequence/xs:element/@name"/>
        <xsl:element name="{$firstContext}">
          <xsl:for-each select="$xsdElems">
            <xsl:variable name="secondContext" select="."/>
            <xsl:element name="{$secondContext}">
              <xsl:value-of select="$input/*/*[@name=$secondContext]/@value"/>
            </xsl:element>
          </xsl:for-each>      
        </xsl:element>
      </xsl:template>
    
    </xsl:stylesheet>
    

    XML Output:

    <person>
       <firstname>1234567890</firstname>
       <lastname>The BFG</lastname>
       <address>5</address>
    </person>
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题