按属性排序XML和修改

时间:2011-07-08 21:36:22

标签: xml sorting xslt open-source

首先是一个小小的背景:我使用收集管理软件GCStar来管理我的数字图书馆(漫画/漫画/电影,你可以说它 - 除了书籍之外它非常棒)。问题是,它不允许我按多个键对架子进行排序,比如系列和剧集编号。稍后添加的剧集将始终显示在书架中较低的位置,按系列分组。

我对配置进行了讨论,发现它使用的.gcs文件只不过是一个XML(我只是很熟悉)。是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<collection type="GCTVepisodes" items="101" version="1.6.1">
 <information>
  <maxId>101</maxId>
 </information>

 <item
  id="1"
  name="The Vice President Doesn't Say Anything about the Possibility of 
        Him Being the Main Character"
  series="Baccano"
  season="1"
  episode="1"
  ...
 >
  <synopsis>It's 1931 and...</synopsis>
 ...
 </item>
 <item ...

根据我的理解,该程序将始终按ID降序(每当我添加一集时会增加)。所以我需要对此进行改造:

  1. 按系列排序XML,然后是季节,然后是剧集
  2. 相应地更改id属性,从1开始到结束(也基于此重置maxId)
  3. 将其全部写成与另一个XML相同的格式。
  4. 如何做到这一点(显然这里没有谈论剪切代码)? XSLT可以做所有这些吗?我应该在Perl中查看基于树的解析器吗?这是周末,我在Linux机器上,所以在UNIX上运行的开源解决方案会很好 - Perl中的东西可能是最好的。我该怎么读?

    如果我不能在家里这样做,那么,我总是可以在办公室设计一个小型的数据存储工作,但我真的很喜欢一个更简单的解决方案。

    谢谢! :)

4 个答案:

答案 0 :(得分:2)

maxId(和集合中的项目)值不应更改,因为您没有删除或添加ID。

如果您想要一个简单的命令行开源XSLT转换器,请使用libxml2 / libxslt中的XSLTProc。它几乎适用于所有标准的Linux。 http://xmlsoft.org/XSLT/xsltproc2.html

使用此命令xsltproc transform.xsl input.xml >output.xml

这是一个解决方案,XSLT转换样式表,应该可以工作;-)(我有足够的空闲时间来编写它)

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

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

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

<!-- remove items, they will be sorted and inserted later -->
<xsl:template match="/collection/item"/>

<!-- remove id -->
<xsl:template match="/collection/item/@id"/>

<xsl:template match="/collection">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <!-- copy and sort item by series, then season, then episode -->
        <xsl:for-each select="item">
            <xsl:sort select="@series" data-type="text"/>
            <xsl:sort select="@season" data-type="number"/>
            <xsl:sort select="@episode" data-type="number"/>
            <xsl:copy>
                <xsl:attribute name="id">
                    <xsl:value-of select="position()"/>
                </xsl:attribute>
                <!-- copy the rest of item -->
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

我用这个简化的数据来测试它:

<?xml version="1.0" encoding="UTF-8"?>
<collection type="GCTVepisodes" items="5" version="1.6.1">
 <information>
  <maxId>5</maxId>
 </information>

 <item
  id="1"
  name="The Vice President Doesn't Say Anything about the Possibility of 
        Him Being the Main Character"
  series="Baccano"
  season="1"
  episode="1"/>

 <item
  id="2"
  name="blabla"
  series="c"
  season="1"
  episode="2"/>

 <item
  id="3"
  name="abc"
  series="Baccano"
  season="2"
  episode="1"/>  

 <item
  id="4"
  name="blabla2"
  series="Baccano"
  season="1"
  episode="2"/>

 <item
  id="5"
  name="first of c"
  series="c"
  season="1"
  episode="1"/>

</collection>

这就是结果(看看位置和ID如何变化):

<?xml version="1.0" encoding="UTF-8"?>
<collection type="GCTVepisodes" items="5" version="1.6.1">
  <information>
    <maxId>5</maxId>
  </information>
  <item id="1" name="The Vice President Doesn't Say Anything about the Possibility of    Him Being the Main Character" series="Baccano" season="1" episode="1"/>
  <item id="2" name="blabla2" series="Baccano" season="1" episode="2"/>
  <item id="3" name="abc" series="Baccano" season="2" episode="1"/>
  <item id="4" name="first of c" series="c" season="1" episode="1"/>
  <item id="5" name="blabla" series="c" season="1" episode="2"/>
</collection>

答案 1 :(得分:1)

您可以使用两个简单的模板获得相同的结果:

  • 在第一个模板(标识)中,我们可以稍微“定位”应用模板机制,以便对item元素进行排序。
  • 在第二个模板中,我们可以覆盖每个item元素,并使用position()函数重新计算id属性。我们将保留所有其他后代节点,但不包括id的原始item

使用Saxon 6.5.5测试XSLT 1.0转换

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(self::item)]"/>
            <xsl:apply-templates select="item">
                <xsl:sort select="@series"/>
                <xsl:sort select="@season" data-type="number"/>
                <xsl:sort select="@episode" data-type="number"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="item">
        <item id="{position()}">
            <xsl:apply-templates select="@*[name()!='id']|node()"/>
        </item>
    </xsl:template>

</xsl:stylesheet>

当以上变换应用于以下输入时(@therealmarv稍微修改为包含子元素):

<collection type="GCTVepisodes" items="5" version="1.6.1">
    <information>
        <maxId>5</maxId>
    </information>
    <item
        id="1"
        name="The Vice President Doesn't Say Anything about the Possibility of 
        Him Being the Main Character"
        series="Baccano"
        season="1"
        episode="1">
        <synopsis>It's 1931 and...</synopsis>
    </item>
    <item
        id="2"
        name="blabla"
        series="c"
        season="1"
        episode="2">
        <synopsis>It's 1931 and...</synopsis>
    </item>
    <item
        id="3"
        name="abc"
        series="Baccano"
        season="2"
        episode="1">
        <synopsis>It's 1931 and...</synopsis>
    </item>
    <item
        id="4"
        name="blabla2"
        series="Baccano"
        season="1"
        episode="2">
        <synopsis>It's 1931 and...</synopsis>
    </item>
    <item
        id="5"
        name="first of c"
        series="c"
        season="1"
        episode="1">
        <synopsis>It's 1931 and...</synopsis>
    </item>
</collection>

生成以下输出:

<collection type="GCTVepisodes" items="5" version="1.6.1">
   <information>
      <maxId>5</maxId>
   </information>
   <item id="1" name="The Vice President Doesn't Say Anything about the Possibility of    Him Being the Main Character" series="Baccano" season="1" episode="1">
      <synopsis>It's 1931 and...</synopsis>
   </item>
   <item id="4" name="blabla2" series="Baccano" season="1" episode="2">
      <synopsis>It's 1931 and...</synopsis>
   </item>
   <item id="3" name="abc" series="Baccano" season="2" episode="1">
      <synopsis>It's 1931 and...</synopsis>
   </item>
   <item id="5" name="first of c" series="c" season="1" episode="1">
      <synopsis>It's 1931 and...</synopsis>
   </item>
   <item id="2" name="blabla" series="c" season="1" episode="2">
      <synopsis>It's 1931 and...</synopsis>
   </item>
</collection>

答案 2 :(得分:0)

  

XSLT可以做所有这些吗?

是。见下面的子答案

  
      
  • 按系列排序XML,然后是季节,然后是剧集
  •   

是的,您可以使用XSLT对XML进行排序。

http://www.w3schools.com/xsl/xsl_sort.asp

  
      
  • 相应地更改id属性,从1开始到结束(也基于此重置maxId)
  •   

您还可以使用它来编写您想要的任何文本。这意味着您可以替换转换中的数据。

它还可以assign variables,执行if statementsloops,执行XPath查询,拥有built-in function library等等,因此对于您来说它将足够强大想做。

  
      
  • 将其全部写入与另一种XML相同的格式
  •   

...这也意味着你可以用它来编写XML

  

我应该阅读什么?

XSLT:)

w3schools链接(上面的所有链接)对我来说很充实,但我一般都熟悉XML结构(属性,元素,根元素,内部文本等)。如果您熟悉它,请阅读XSLT。

您还可以查看XmlStarlet,这是一个用于从命令行或shell脚本/批处理文件查询和转换XML的工具(但对于转换,它可能无论如何都使用XSLT)。

答案 3 :(得分:0)

我也会用XSLT做这件事。但是,我的样式表与therealmarv的样式表略有不同。

此XML输入:

<collection type="GCTVepisodes" items="101" version="1.6.1">
  <information>
    <maxId>101</maxId>
  </information>

  <item
    id="1"
    name="The Vice President Doesn't Say Anything about the Possibility of 
    Him Being the Main Character"
    series="Baccano"
    season="1"
    episode="2"
    >
    <synopsis>Blah blah blah...</synopsis>
    ...
  </item>

  <item
    id="2"
    name="some name"
    series="Alpha"
    season="2"
    episode="1"
    >
    <synopsis>Blah blah blah...</synopsis>
    ...
  </item>


  <item
    id="3"
    name="The Vice President Doesn't Say Anything about the Possibility of 
    Him Being the Main Character"
    series="Baccano"
    season="1"
    episode="1"
    >
    <synopsis>It's 1931 and...</synopsis>
    ...
  </item>

  <item
    id="4"
    name="some name"
    series="Alpha"
    season="1"
    episode="1"
    >
    <synopsis>Blah blah blah...</synopsis>
    ...
  </item>

</collection>

使用此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="collection">
    <collection>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="information"/>
      <xsl:apply-templates select="item">
        <xsl:sort select="@series" data-type="text"/>
        <xsl:sort select="@season" data-type="number"/>
        <xsl:sort select="@episode" data-type="number"/>
      </xsl:apply-templates>      
    </collection>
  </xsl:template>

  <xsl:template match="item">
    <item id="{position()}">
      <xsl:apply-templates select="@*[not(name()='id')]|node()"/>
    </item>
  </xsl:template>

</xsl:stylesheet>

生成此输出:

<collection type="GCTVepisodes" items="101" version="1.6.1">
   <information>
      <maxId>101</maxId>
   </information>
   <item id="1" name="some name" series="Alpha" season="1" episode="1">
      <synopsis>Blah blah blah...</synopsis>
    ...
  </item>
   <item id="2" name="some name" series="Alpha" season="2" episode="1">
      <synopsis>Blah blah blah...</synopsis>
    ...
  </item>
   <item id="3" name="The Vice President Doesn't Say Anything about the Possibility of      Him Being the Main Character" series="Baccano" season="1" episode="1">
      <synopsis>It's 1931 and...</synopsis>
    ...
  </item>
   <item id="4" name="The Vice President Doesn't Say Anything about the Possibility of      Him Being the Main Character" series="Baccano" season="1" episode="2">
      <synopsis>Blah blah blah...</synopsis>
    ...
  </item>
</collection>

根据thealmarv的回答输出:

<collection type="GCTVepisodes" items="5" version="1.6.1">
   <information>
      <maxId>5</maxId>
   </information>
   <item id="1" name="The Vice President Doesn't Say Anything about the Possibility of      Him Being the Main Character" series="Baccano" season="1" episode="1"/>
   <item id="2" name="blabla2" series="Baccano" season="1" episode="2"/>
   <item id="3" name="abc" series="Baccano" season="2" episode="1"/>
   <item id="4" name="first of c" series="c" season="1" episode="1"/>
   <item id="5" name="blabla" series="c" season="1" episode="2"/>
</collection>