简体   繁体   English

如何转换xml中的无序元素以匹配xsd:sequence顺序?

[英]How to transform unordered elements in xml to match an xsd:sequence order?

in contrast to the answered situation of equal named child elements I'm trying to convert the following: 同等命名子元素的答案相比,我尝试转换以下内容:

<Person>
    <Address>5</Address>
    <Firstname>1234567890</Firstname>
    <Lastname>
        <MaidenName>The BFG</MaidenName>
        <StageName>GFB eht</StageName>
    </Lastname>
</Person>

into the wanted result: 进入所需结果:

<Person>    
    <Firstname>1234567890</Firstname>
    <Lastname>
        <StageName>GFB eht</StageName>
        <MaidenName>The BFG</MaidenName>    
    </Lastname>
    <Address>5</Address>    
</Person>

But keep getting the following error: 但是继续出现以下错误:
Error at xsl:element on line 47 of xml2xml.xsl: XTDE0820: Supplied element name is a zero-length string xml2xml.xsl的第47行的xsl:element错误:XTDE0820:提供的元素名称是零长度的字符串

How can the elements passed and inserted in the correct order? 元素如何以正确的顺序传递和插入?

the wanted result is complying to the provided XML schema: 所需结果符合所提供的XML模式:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="Person" type="Person"/>
   <xs:complexType name="Person">
      <xs:sequence>
        <xs:element name="Firstname" type="xs:string"/>
        <xs:element name="Lastname" type="Lastname"/>
        <xs:element name="Address" type="xs:string"/>
      </xs:sequence>
   </xs:complexType>
  <xs:complexType name="Lastname">
    <xs:sequence>
        <xs:element name="StageName" type="xs:string"/>
        <xs:element name="MaidenName" type="xs:string"/>
    </xs:sequence>
   </xs:complexType>
</xs:schema>

I'm using this transformation, which I got from Can you transform unordered xml to match an xsd:sequence order? 我正在使用这种转换,它是从获得的, 您可以转换无序xml以匹配xsd:sequence顺序吗? xsdsequence-order and tried to adapt: xsdsequence-order并尝试适应:

<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:complexType" use="@name"/>

 <xsl:variable name="vSchema" select=
 "document('file:///D:/xslt/test/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:complexType">
  <xsl:param name="pElement"/>

  <xsl:element name="{name($pElement)}">
       <xsl:apply-templates mode="generate"
            select="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/*[local-name(.) = local-name(current())]/*"/>
      <xsl:element name="{local-name($vProp)}">
       <xsl:value-of select="$vProp"/>
   </xsl:element>
 </xsl:template>

</xsl:stylesheet>

I think this is fundamentally misguided. 我认为这从根本上是错误的。 Either you want a general solution that works with any schema, or you want a solution that's oriented to one particular schema. 您想要一种适用于任何模式的通用解决方案,或者想要一种针对某个特定模式的解决方案。 At the moment, you're trying to extract the structure from the schema, but you've made so many assumptions about the way the schema is written that your solution is hopelessly fragile (and I'm not even going to try to find the particular bug). 目前,您正在尝试从架构中提取结构,但是您已经对架构的编写方式做出了许多假设,以至于解决方案非常脆弱(我什至不会尝试找到该架构)。特定错误)。 You've assumed, for example, that: 例如,您假设:

  • The names of complex types match the names of elements 复杂类型的名称与元素的名称匹配
  • The schema doesn't use include or import 模式不使用包含或导入
  • The complex type contains an xs:sequence, and the xs:sequence contains element particles only 复杂类型包含xs:sequence,而xs:sequence仅包含元素粒子
  • All elements in the sequence occur exactly once 序列中的所有元素仅发生一次
  • The element particles are local element declarations rather than references to global element declarations 元素粒子是局部元素声明,而不是对全局元素声明的引用

These assumptions are so constraining that you really might as well hard-code the rules in the XSLT code rather than trying to extract them from the schema. 这些假设是如此严格,以至于您真的可以将规则硬编码为XSLT代码,而不是尝试从架构中提取它们。

If you want to do this properly, don't try to work from the source schema document, work instead from the output of a schema compiler - for example the SCM files produced by the Saxon schema processor, or the schema information that can be accessed in XSLT using the saxon:schema() extension function. 如果要正确执行此操作,请不要尝试从源模式文档中进行工作,而应从模式编译器的输出中进行工作-例如,Saxon模式处理器生成的SCM文件或可以访问的模式信息。在XSLT中使用saxon:schema()扩展功能。

I'm not sure that this is really what you are expecting, but anyway - creating the desired output structure would be achieved this way: 我不确定这是否真的是您所期望的,但是无论如何-通过这种方式可以创建所需的输出结构:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xsl:template match="Person">
    <xsl:element name="Person">
        <xsl:copy-of select="Firstname" />
        <xsl:copy-of select="Lastname" />
        <xsl:copy-of select="Address" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

The ordering is achieved by reconstructing the required elements and copying all subnodes. 通过重构所需元素并复制所有子节点来实现排序。

Assuming you just wanted to adjust the ordering based on the XSD, try the XSLT below. 假设您只是想根据XSD调整顺序,请尝试以下XSLT。 For each element in the source XML, it checks to find an xs:element in the XSD, and for that xs:element whether is a complex type. 对于源XML中的每个元素,它都会检查以在XSD中查找xs:element ,对于该xs:element是否为复杂类型。 If so, it then orders the child elements accordingly. 如果是这样,那么它将相应地对子元素进行排序。

This is still very fragile, as per Michael. 根据迈克尔的说法,这仍然非常脆弱。 Kay's answer, although it no longer relies on the name of the complex type matching the name of the element. 凯的答案,尽管它不再依赖于与元素名称匹配的复杂类型的名称。

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" indent="yes" />

    <xsl:key name="kxsElemByName" match="xs:element" use="@name"/>
    <xsl:key name="kxsTypeByName" match="xs:complexType" use="@name"/>

    <xsl:variable name="vSchema" select="document('file:///D:/xslt/test/schema.xsd')"/> 

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

    <xsl:template match="*" priority="2">
        <xsl:variable name="current" select="." />
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:for-each select="$vSchema">
                <xsl:variable name="element" select="key('kxsElemByName', name($current))" />
                <xsl:variable name="complex" select="key('kxsTypeByName', $element/@type)" />
                <xsl:choose>
                <xsl:when test="$complex">
                    <xsl:for-each select="$complex/xs:sequence/xs:element">
                        <xsl:apply-templates select="$current/*[name() = current()/@name]" />
                    </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="$current/node()"/>
                </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM