我需要添加缺少的节点,然后排序包括添加的新节点

Posted

技术标签:

【中文标题】我需要添加缺少的节点,然后排序包括添加的新节点【英文标题】:I need to add missing node and then sort including the added new node 【发布时间】:2018-03-17 20:45:17 【问题描述】:

我有一个输入 XML,我需要使用 XSLT 2.0 对其进行转换。

这是我的输入 xml

<root>
         <person>
            <LastName>yyyyy</LastName>
            <FirstName>xxxx</FirstName>
            <profession>IT/xx</profession>
            <area>0000</area>
            <email>xxx.yyy@xxx.com</email>
            <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
            <sex>male</sex>
         </person>
      </root>

我的输入 xml 缺少一些节点。例如(移动设备、国家/地区,根据 xsd 是强制性的)。现在,我需要添加这两个节点(作为空节点),然后我需要 sort 包括这两个节点。

排序顺序应为:名字、姓氏、电子邮件、性别、职业、手机、地址、地区、国家;

最终输出的 xml 应该是:

<root>
         <person>
            <FirstName>xxxx</FirstName>
            <LastName>yyyyy</LastName>
            <email>xxx.yyy@xxx.com</email>
            <sex>male</sex>
            <profession>IT/xx</profession>
            <mobile/>
            <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
            <area>0000</area>
            <country/>
         </person>
      </root>

我尝试使用以下 XSLT:

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

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pOrderedNames" select="'FirstName,LastName,email,sex,profession,mobile,Address,area,country'"/>

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

    <xsl:template match="person[not(./country)]" mode="country">
        <xsl:element name="country" />
    </xsl:template>

    <xsl:template match="person[not(./mobile)]" mode="mobile">
        <xsl:element name="mobile" />
    </xsl:template>

    <xsl:template match="*" mode="sorter">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="string-length(substring-before($pOrderedNames,concat(',',name(),',')))"/>  
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="person">
        <xsl:copy>
            <xsl:apply-templates select="node()"/>
            <xsl:apply-templates  select="self::node()" mode="mobile"/>
            <xsl:apply-templates  select="self::node()" mode="country"/>
            <xsl:apply-templates  select="self::node()" mode="email"/>
            <xsl:apply-templates  select="self::node()" mode="sex"/>    
            <xsl:apply-templates select="self::*" mode="sorter"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

使用上面的 XSLT,它被转换如下:

<root>
   <person>
      <LastName>yyyyy</LastName>
      <FirstName>xxxx</FirstName>
      <profession>IT/xx</profession>
      <area>0000</area>
      <email>xxx.yyy@xxx.com</email>
      <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
      <sex>male</sex>
      <mobile/>
      <country/>yyyyyxxxxIT/xx0000xxx.yyy@xxx.comaaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeeemaleyyyyyxxxxIT/xx0000xxx.yyy@xxx.comaaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeeemale<person>
         <FirstName>xxxx</FirstName>
         <LastName>yyyyy</LastName>
         <email>xxx.yyy@xxx.com</email>
         <sex>male</sex>
         <profession>IT/xx</profession>
         <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
         <area>0000</area>
      </person>
   </person>
</root>

有人可以帮我看看吗?

提前致谢。

【问题讨论】:

请展示您为编写 XSLT 所做的努力。这是一个简单的转换,可以通过学习 XSLT 基础知识来实现​​。 您好 Aniket,感谢您的回复。我已经包含了我尝试过的 XSLT。我得到的结果几乎就在附近,但是输出 xml 中出现了一些重复的数据。可以请你看一下吗。 您好 Aniket,当我在输入 xml 中没有 标记时它可以工作,但如果有 标记则它不工作。我在下面添加了详细信息。您能帮帮我吗? 我没有提供任何答案。请检查@Martin Honnen 提供的答案并做出相应的回应。 【参考方案1】:

这是对您的方法的一种改编,它使用模板创建新元素,并通过将各种应用模板包装到 perform-sort 中来对它们进行排序:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">   

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pOrderedNames"
        select="'FirstName,LastName,email,sex,profession,mobile,Address,area,country'"/>
    <xsl:param name="pNameOrder" select="tokenize($pOrderedNames, ',')"/>

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

    <xsl:template match="person" mode="country">
        <country/>
    </xsl:template>

    <xsl:template match="person" mode="mobile">
        <mobile/>
    </xsl:template>

    <xsl:template match="person">
        <xsl:copy>
            <xsl:perform-sort>
                <xsl:sort select="index-of($pNameOrder, local-name())"/>
                <xsl:apply-templates select="*"/>
                <xsl:apply-templates select=".[not(country)]" mode="country"/>
                <xsl:apply-templates select=".[not(mobile)]" mode="mobile"/>
            </xsl:perform-sort>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我不确定创建新元素需要模板和模式,简单的&lt;xsl:if test="not(mobile)"&gt;&lt;mobile/&gt;&lt;/xsl:if&gt; 就足够了,但使用模式是一种可能的方式。

【讨论】:

嗨,马丁,感谢您的回复。当我在输入 xml 中没有 标记时它可以工作,但是如果有 标记,那么它就不能工作。我在下面添加了详细信息。请你帮助我好吗。提前致谢。 @krishh,我已经编辑了答案并更改为代码仅在子元素不存在时将模板应用于 person 元素本身,这应该可以避免您遇到的问题,它在xsl:if 测试中简单地创建新元素可能更容易。

以上是关于我需要添加缺少的节点,然后排序包括添加的新节点的主要内容,如果未能解决你的问题,请参考以下文章

学会堆排序只需要几分钟

p:tree ajax 加载节点不显示展开图标

c++ 手写堆 (包括建堆排序添加元素删除元素)

添加新节点到现有Kubernetes集群

检查节点中缺少的选项和回调

检查节点中缺少的选项和回调