【问题标题】:XSLT sort without specific attributes没有特定属性的 XSLT 排序
【发布时间】:2016-03-29 01:26:17
【问题描述】:

首先祝大家节日快乐!好的,所以我最近开始了一个新项目,该项目需要我处理一个庞大的 XML 文件,大约 20k 行并且很难使用,因为它没有排序并且导致节点中出现很多重复的属性,我想要解决这个问题。使用 XSLT + Notepad++ 应该会让这件事变得简单,除非我很难找到有关 XSLT 排序的信息,因为您不知道每个属性及其格式如<property name="" />

文件的样子:

<Items>
     <item id="1">
          <prop name="c">
          <prop name="a">
          <event name="c">
          <event name="a">
          <prop class="b">
               <prop name="a">
               <prop name="c">
          </prop>
          <prop class="a">
               <prop name="b">
               <prop name="a">
          </prop>
     </item>
</items>

我希望文件是什么样子的:

<Items>
     <item id="1">
          <prop name="a">
          <prop name="b">
          <event name="a">
          <event name="c">
          <prop class="a">
               <prop name="a">
               <prop name="c">
          </prop>
          <prop class="b">
               <prop name="a">
               <prop name="b">
          </prop>
     </item>
</items>

我只想通过属性的值来整理&lt;prop name..&gt;,然后在&lt;prop class&gt;中进行同样的事情

-- 更新-- 好的,所以我不想发布实际 xml 的一部分,因为它通常很容易交换代码,但是我在过去的几个小时里一直在玩这个,似乎无法让它工作。

这里来自 XML 的三种不同类型的项目。

<block id="1" name="stone">
    <property name="Material" value="stone"/>
    <property name="Shape" value="Terrain"/>
    <property name="Mesh" value="terrain"/>
    <property name="Texture" value="1"/>
    <property name="Weight" value="100"/>
    <property name="DropScale" value="2"/>
    <property name="LPHardnessScale" value="2"/>
    <drop event="Harvest" name="rockSmall" count="125"/>
    <drop event="Harvest" name="ironFragment" count="5"/>
    <drop event="Destroy" name="rockSmall" count="50"/>
    <drop event="Fall" name="destroyedStone" count="1" prob="1.0" stick_chance=".75"/>
</block>

<block id="154" name="metalReinforcedWoodWedge60">
    <property name="Material" value="metal"/>
    <property name="Shape" value="Wedged60Full"/>
    <property name="Texture" value="380"/>
    <property name="Collide" value="movement,rocket,melee"/>
    <property name="FuelValue" value="100"/>
    <drop event="Destroy" name="woodDebris" count="1"/>
    <property name="CanMobsSpawnOn" value="false"/>
    <drop event="Fall" name="woodDebris" count="1" prob="1.0" stick_chance=".75"/>
    <property class="UpgradeBlock">
        <property name="ToBlock" value="scrapIronWedge60"/>
        <property name="Item" value="scrapIron"/>
        <property name="ItemCount" value="10"/>
        <property name="UpgradeHitCount" value="4"/>
    </property>
    <property name="DowngradeBlock" value="reinforcedWoodWedge60"/>
    <property class="RepairItems">
        <property name="scrapIron" value="10"/>
    </property>
    <property name="Group" value="Building,Basics"/>
</block>

<block id="1146" name="cottonYoung">
    <property name="Class" value="PlantGrowing"/>
    <property name="Material" value="plants"/>
    <property name="Shape" value="BillboardPlant"/>
    <property name="Mesh" value="grass"/>
    <property name="Texture" value="20"/>
    <property name="Collide" value="melee"/>
    <property name="CanDecorateOnSlopes" value="true"/>
    <property name="IsTerrainDecoration" value="true"/>
    <property class="PlantGrowing">
        <property name="Next" value="cotton"/>
        <property name="GrowthRate" value="60"/>
        <property name="IsRandom" value="false"/>
        <property name="FertileLevel" value="1"/>
    </property>
</block>

【问题讨论】:

  • 你能解释一下你想对数据中的哪个值进行排序吗?对于您发布的示例,结果会如何(好吧,如果您首先将其修复为格式良好的 XML)?
  • 您尚未关闭单个 prop 元素。是否有&lt;prop name="b"/&gt;&lt;event name="c"/&gt;&lt;prop name="a"/&gt;,然后您是否想将prop 元素与name 属性组合在一起,例如得到&lt;prop name="a"/&gt;&lt;prop name="b"/&gt;&lt;event name="c"/&gt;?还是只想对相邻的propevent 元素进行排序?
  • 如今,随着 XML 的发展,20K 行已经非常少了。

标签: xml xslt


【解决方案1】:

假设 XSLT 2.0(需要 Saxon 9、XmlPrime 或其他 XSLT 2.0 处理器)和类似的输入

<Items>
     <item id="1">
          <prop name="c"/>
          <prop name="a"/>
          <event name="c"/>
          <event name="a"/>
          <prop class="b">
               <prop name="a"/>
               <prop name="c"/>
          </prop>
          <prop class="a">
               <prop name="b"/>
               <prop name="a"/>
          </prop>
     </item>
</Items>

代码

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

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

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

<xsl:template match="item[prop] | prop[prop]">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:for-each-group select="*" group-adjacent="node-name(.)">
      <xsl:apply-templates select="current-group()">
        <xsl:sort select="@*"/> <!-- if there can be more than one attribute on a single child make that select="@name | @class" -->
      </xsl:apply-templates>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

创建结果

<Items>
   <item id="1">
      <prop name="a"/>
      <prop name="c"/>
      <event name="a"/>
      <event name="c"/>
      <prop class="a">
         <prop name="a"/>
         <prop name="b"/>
      </prop>
      <prop class="b">
         <prop name="a"/>
         <prop name="c"/>
      </prop>
   </item>
</Items>

根据您的新示例,执行工作的模板将更改为

<xsl:template match="block[property] | property[property]">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:for-each-group select="*" group-adjacent="node-name(.)">
      <xsl:apply-templates select="current-group()">
        <xsl:sort select="@name | @class"/>
      </xsl:apply-templates>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

【讨论】:

  • 您的代码非常适合我放置的示例!你太棒了,但是我无法调整它以使其与我的实际 XML 一起工作。我认为示例代码很好地代表了实际的 XML,但它似乎不能正常工作。
  • 好吧,像&lt;property name="FuelValue" value="100"/&gt; &lt;drop event="Destroy" name="woodDebris" count="1"/&gt; &lt;property name="CanMobsSpawnOn" value="false"/&gt; &lt;drop event="Fall" name="woodDebris" count="1" prob="1.0" stick_chance=".75"/&gt; 这样的序列会发生什么,你想将&lt;property name="CanMobsSpawnOn" value="false"/&gt; 移动到&lt;property name="FuelValue" value="100"/&gt; 之前吗?
  • @user1451070,我已经编辑了答案以显示如何使其适应您添加到问题中的代码。上一条评论中概述的问题仍然存在,我不确定您要如何对混合的元素序列进行排序。
  • 羞愧地垂头丧气 是的,我想尝试了所有我能想到的组合,却忘记了我将“块”改为“项目”......
  • 仍有问题,当属性块包含“”时,它看起来不想对其进行排序。我想有没有办法按组排序?例如将所有“”分组/排序,将“”分组/排序,然后将drop放在一起。
【解决方案2】:

也许是这样的:

<xsl:stylesheet>
  <xsl:template match="/">
    <xsl:for-each select="//item">
      <xsl:for-each select="prop">
        <xsl:sort select="@name | @class">

这可能不是您想要的,但您的问题也有点模糊。不过,这应该为您提供了一个足够好的起点来构建自己的解决方案。

【讨论】:

    【解决方案3】:

    如果您想对&lt;item&gt; 节点中的&lt;prop&gt; 节点进行排序,还想对&lt;prop class="..."&gt; 节点中的&lt;prop&gt; 节点进行排序,这里有一种方法:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="xml" version="1.0" encoding="ISO-8859-1"/>
    
    <xsl:template match="Items">
    <root>
        <xsl:apply-templates select="item">
        </xsl:apply-templates>
    </root>
    </xsl:template>
    
    <xsl:template match="item">
        <item id="{@id}">
        <xsl:apply-templates select="prop">
            <xsl:sort select="@name" data-type="text" order="ascending"/>
        </xsl:apply-templates>
        <xsl:apply-templates select="event"/>
        </item>
    </xsl:template>
    
    <xsl:template match="prop|event">
        <xsl:if test="@class">
            <prop class="{@class}">
                <xsl:apply-templates select="prop">
                    <xsl:sort select="@name" data-type="text" order="ascending"/>
                </xsl:apply-templates>
            </prop>
        </xsl:if>
        <xsl:if test="not(@class)">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

      猜你喜欢
      • 2011-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      • 1970-01-01
      • 2018-11-23
      相关资源
      最近更新 更多