使用 xslt 将 xml 结构转换为另一个 xml 结构 [英] Transform xml structure to another xml structure with xslt

查看:40
本文介绍了使用 xslt 将 xml 结构转换为另一个 xml 结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题.我有以下源 xml 文件:

I have a question. I have the following source xml file:

源 xml:

<Container>
  <DataHeader>
    <c id="b" value="TAG" />
    <c id="g" value="Info" /> 
  </DataHeader>
  <Data>
    <Rows>
      <r no="1">
        <c id="b" value="uid1" uid="T.A.uid1" />
        <c id="g" value="uid1|tag1|attr1|somevalue1" />
      </r>
   <r no="1">
        <c id="b" value="uid1" uid="T.A.uid1" />
        <c id="g" value="uid1|tag1|attr2|somevalue2" />
      </r>
      <r no="2">
        <c id="b" value="uid1" uid="T.A.uid1" />
        <c id="g" value="uid1|tag2|attr3|somevalue3" />
      </r>
    <r no="10">
        <c id="b" value="uid2" uid="T.A.uid2" />
        <c id="g" value="uid2|tag1|attr1|somevalue4" />
      </r>
      <r no="11">
        <c id="b" value="uid2" uid="T.A.uid2" />
        <c id="g" value="uid2|tag2|attr3|somevalue5" />
      </r>
   </Rows>
  </Data>
</Container>

id 为 'g' 的元素 'c' 在源 xml 中很重要.这是一个连接字符串,其值由|"分隔.我们需要这些值来制作目标 xml.您可以使用 ID 为b"的元素c"来分隔uid".

The element 'c' with id 'g' is important in the source xml. This is a concatened string which values are seperated by a '|'. We need this values to make the target xml. The element 'c' with id 'b' you can use to separate the 'uid'.

值的示例和解释:

 <c id="g" value="uid1|tag1|attr1|somevalue1" />
 **uid value** | element node | **attribute** | attribute value
 **uid1** | tag1 | **attr1** |somevalue1

具有相同uid"的所有元素必须聚合为 1 个单独的TestTag"元素(请参阅目标 xml).需要将具有相同父元素(例如tag1")的所有属性(attr1、attr2)添加到 1 个元素中.我只能使用 xslt(xpath) 1.0.

Al elements with the same 'uid' have to be aggregated into 1 single "TestTag" element (see target xml). Al attributes (attr1, attr2) with same parent element (for example 'tag1') needs to be added to 1 element. I only can make use of xslt(xpath) 1.0.

转换后的目标xml文件应该是这样的.

The target xml file should look like this after transforming.

xsl转换后的目标xml:

<Container>
 <TestTag>
    <object UID="T.A.uid1" Name="uid1"/>
    <tag1 attr1="somevalue1" attr2="somevalue2"/>
    <tag2 attr3="*somevalue3"/>
 </TestTag>
 <TestTag>
    <Iobject UID="T.A.uid2" Name="uid2"/>
    <tag1 attr1="somevalue4" />
    <tag2 attr3="somevalue5"/>
 </TestTag>
</Container>

将源 xml 转换为目标 xml 有哪些可能的解决方案?我尝试了几件事,但我现在卡住了.

What are possible solutions for transforming source xml to target xml? I tried several things but I'm stuck right now.

推荐答案

这并不完全困难,但由于 substring- 的广泛(但必要)嵌套使用而令人难以置信before()substring-after().

This is not exactly difficult, but is mind-boggling due to extensive (yet necessary) nested use of substring-before() and substring-after().

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <!-- index <c> nodes by their @id + "uid value" -->
  <xsl:key name="kObject"    match="r/c" use="
    concat(@id, '|', @value)
  " />
  <!-- index <c> nodes by their @id + "uid value" -->
  <xsl:key name="kTagByUid"  match="r/c" use="
    concat(@id, '|', substring-before(@value, '|'))
  " />
  <!-- index <c> nodes by their @id + "uid value" + "tag name" -->
  <xsl:key name="kTagByName" match="r/c" use="
    concat(@id, '|',
      substring-before(
        @value, 
        substring-after(substring-after(@value, '|'), '|')
      )
    )
  " />

  <xsl:variable name="vTagId"  select="/Container/DataHeader/c[@value='TAG'][1]/@id" />
  <xsl:variable name="vInfoId" select="/Container/DataHeader/c[@value='Info'][1]/@id" />

  <!-- processing starts here -->
  <xsl:template match="Container">
    <xsl:copy>
      <!-- apply templates to unique <c @id=$vTagId> tags -->
      <xsl:apply-templates mode="tag" select="
        Data/Rows/r/c[@id=$vTagId][
          generate-id()
          =
          generate-id(key('kObject', concat(@id, '|', @value))[1])
        ]
      " />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="c" mode="tag">
    <TestTag>
      <object UID="{@uid}" name="{@value}" />
      <!-- apply templates to unique <c @id="g"> tags -->
      <xsl:apply-templates mode="info" select="
        key('kTagByUid', concat($vInfoId, '|', @value))[
          generate-id()
          =
          generate-id(
            key(
              'kTagByName', 
              concat(@id, '|', 
                substring-before(
                  @value, 
                  substring-after(substring-after(@value, '|'), '|')
                )
              )
            )[1]
          )
        ]
      " />
    </TestTag>
  </xsl:template>

  <xsl:template match="c" mode="info">
    <!-- select 'uid1|tag1|' - it's the key to kTagByName -->
    <xsl:variable name="key"  select="substring-before(@value, substring-after(substring-after(@value, '|'), '|'))" />
    <!-- select 'tag1' - it's the element name -->
    <xsl:variable name="name" select="substring-before(substring-after($key, '|'), '|')" /> 

    <xsl:element name="{$name}">
      <xsl:for-each select="key('kTagByName', concat(@id, '|', $key))">
        <!-- select 'attr1|somevalue1' - it's the attribute definition -->
        <xsl:variable name="attrDef" select="substring-after(@value, $key)" />
        <!-- create an attribute -->
        <xsl:attribute name="{substring-before($attrDef, '|')}">
          <xsl:value-of select="substring-after($attrDef, '|')" />
        </xsl:attribute>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

生成:

<Container>
  <TestTag>
    <object UID="T.A.uid1" name="uid1" />
    <tag1 attr1="somevalue1" attr2="somevalue2"></tag1>
    <tag2 attr3="somevalue3"></tag2>
  </TestTag>
  <TestTag>
    <object UID="T.A.uid2" name="uid2" />
    <tag1 attr1="somevalue4"></tag1>
    <tag2 attr3="somevalue5"></tag2>
  </TestTag>
</Container>

注意这里没有注意重复的属性定义.如果您碰巧有 uid1|tag1|attr1|somevalue1 和更高版本的 uid1|tag1|attr1|othervalue1,那么您最终会得到一个属性:attr1="othervalue1" 因为在 <xsl:for-each> 中,两者都轮到他们,后者获胜(即最终在输出中).

Note that this does not pay attention to duplicate attribute definitions. If you happen to have uid1|tag1|attr1|somevalue1 and later uid1|tag1|attr1|othervalue1, then you will end up with one attribute: attr1="othervalue1" because in the <xsl:for-each> both get their turn, and the latter one wins (i.e. ends up in the output).

也可以满足这一点,它需要一个更多的键和一个更多的 Muenchian 分组,我将把它留给读者作为练习.呵呵.;)

It is possible to cater for that as well, it would require one more key and one more Muenchian grouping, I'm going to leave that as an exercise for the reader. Heh. ;)

这篇关于使用 xslt 将 xml 结构转换为另一个 xml 结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆