xslt 的复杂场景

Posted

技术标签:

【中文标题】xslt 的复杂场景【英文标题】:complex scenario for xslt 【发布时间】:2014-01-27 15:04:23 【问题描述】:

更新包含一些额外的细微差别

我有一个需要一些复杂 XSLT 的 XML 场景。我一直在尝试自己解决这个问题,但到目前为止还没有成功。

首先,这是一个模拟 XML 结构。

<Author id="1234">
  <reviews>poor</reviews>
  <Media>     
     <MediaSet>
         <MediaCode type="CD">474747</MediaCode>
     </MediaSet>
     <MediaSet>
       <MediaCode type="CD">535353</MediaCode>
     </MediaSet>
     <MediaSet> 
        <MediaCode type="eBook">989898</MediaCode>
     </MediaSet>
     <MediaSet>
        <MediaCode type="download">202020</MediaCode>
     </MediaSet>
     <MediaSet>
         <MediaCode type="book">161616</MediaCode>
     </MediaSet>
     <MediaSet>
         <MediaCode type="DVD">828282</MediaCode>
     </MediaSet>
     <MediaSet>
         <OtherCode type="widget" number="747474"/>   <!--note different element name and structure-->
     </MediaSet>
   </Media>
  <name>JimBob</name>
</Author>

这个例子是我正在处理的一个非常简化的版本,但我想为数据库导入创建一个看起来像这样的输出:

<row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="CD">474747</field>
    <field name="name">JimBob</field>
</row>
<row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="CD">535353</field>
    <field name="name">JimBob</field>
</row>
<row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="eBook">989898</field>
    <field name="name">JimBob</field>
</row>
<row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="download">989898</field>
    <field name="name">JimBob</field>
</row>
<row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="widget">555555</field>
    <field name="name">JimBob</field>
</row>
CD、电子书、下载可以发生零次或多次。 我需要为每个实例创建一个单独的行 我想忽略某些元素(例如“书”、“DVD”等)。 有 100,000 名“作者”,每个人都有自己独特的“媒体代码”组合。 我需要从中提取数据的混合元素

我有代码将它从起始 XML 结构转换为行/字段 XML 结构,以便数据库导入工作正常,我正在处理的问题是遍历 XML 并在存在多个数据点时创建多行。

这是可以单独使用 XSLT 管理的,还是我必须使用另一种语言来处理?

值得注意的是,我正在处理的 XML 文件的结构要复杂得多,大约为 325MB

【问题讨论】:

如果您已经“拥有将其从起始 XML 结构转换为行/字段 XML 结构的代码”,请在此处发布(一个 sn-p,如果它的整体太长)。 我已经“按原样”回答了您的问题。但是,恕我直言,在将结构导入数据库之前将其展平是错误的。假设是一个关系数据库,最好将数据导入两次,分别导入两个单独的表:Authors 和 Media,而不是一个包含大量重复重复相同数据的平面表。 另外,您应该有一个媒体类型字段,其中“CD”、“Ebook”等是,另一个字段是媒体代码,通用于所有类型。否则搜索数据库会变得很麻烦。 【参考方案1】:

首先,您的示例输入和输出均无效。由于开始和结束标签之间存在大量不匹配,因此输入尤其如此,例如:

<MediaCode type="eBook">989898</Type>

假设一个正确的输入,其中还包括一个根元素:

<Authors>
    <Author id="1234">
       <reviews>poor</reviews>
       <MediaSet>
          <MediaCode type="CD">101</MediaCode>
          <MediaCode type="CD">102</MediaCode>
          <MediaCode type="eBook">111</MediaCode>
          <MediaCode type="download">121</MediaCode>
          <MediaCode type="book">131</MediaCode>
       </MediaSet>
       <name>Adam</name>
    </Author>
    <Author id="5678">
       <reviews>good</reviews>
       <MediaSet>
          <MediaCode type="CD">201</MediaCode>
          <MediaCode type="eBook">202</MediaCode>
          <MediaCode type="download">203</MediaCode>
          <MediaCode type="book">204</MediaCode>
       </MediaSet>
       <name>Betty</name>
    </Author>
</Authors>

你可以使用这样的样式表:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/">
<rows>
    <xsl:for-each select="Authors/Author/MediaSet/MediaCode[@type!='book']">
        <row>
            <field name="authorID"><xsl:value-of select="../../@id" /></field>
            <field name="reviews"><xsl:value-of select="../../reviews" /></field>
            <field name="@type"><xsl:value-of select="." /></field>
            <field name="name"><xsl:value-of select="../../name" /></field>
        </row>
    </xsl:for-each>
</rows>
</xsl:template>
</xsl:stylesheet>

产生以下结果(同样,带有一个根元素):

<?xml version="1.0" encoding="utf-8"?>
<rows>
  <row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="CD">101</field>
    <field name="name">Adam</field>
  </row>
  <row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="CD">102</field>
    <field name="name">Adam</field>
  </row>
  <row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="eBook">111</field>
    <field name="name">Adam</field>
  </row>
  <row>
    <field name="authorID">1234</field>
    <field name="reviews">poor</field>
    <field name="download">121</field>
    <field name="name">Adam</field>
  </row>
  <row>
    <field name="authorID">5678</field>
    <field name="reviews">good</field>
    <field name="CD">201</field>
    <field name="name">Betty</field>
  </row>
  <row>
    <field name="authorID">5678</field>
    <field name="reviews">good</field>
    <field name="eBook">202</field>
    <field name="name">Betty</field>
  </row>
  <row>
    <field name="authorID">5678</field>
    <field name="reviews">good</field>
    <field name="download">203</field>
    <field name="name">Betty</field>
  </row>
</rows>

【讨论】:

感谢您的帮助,我已经修改了问题以反映原始示例中未体现的额外复杂性。 @user859501 这是一个相当微不足道的更改:只需在 for-each 路径中使用 * 代替 MediaCode。我还重申我的建议,根本不要这样做。【参考方案2】:

AFAIKS,这一点都不复杂:

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

  <xsl:output indent="yes"/>

  <xsl:template match="/">
    <output>
      <xsl:apply-templates/>
    </output>
  </xsl:template>

  <xsl:template match="text()"/>

  <xsl:template match="Author/MediaSet/MediaCode[@type!='book']">
    <row>
      <field name="authorId"><xsl:value-of select="../../@id"/></field>
      <field name="reviews"><xsl:value-of select="../../reviews"/></field>
      <field name="@type"><xsl:value-of select="."/></field>
      <field name="name"><xsl:value-of select="../../name"/></field>
    </row>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

感谢 cmets,我已经修改了任务以反映原始示例中未体现的额外复杂性。

以上是关于xslt 的复杂场景的主要内容,如果未能解决你的问题,请参考以下文章

XSLT 转换不适用于复杂的 XML

使用 Python 或 XSLT 将复杂的 XML 转换为 CSV

使用 xslt 将 xml 复杂节点元素拆分为多个节点

在 XSLT 中对记录进行分组时如何避免 O(n^2) 复杂性?

如何使用 xslt 获取与复杂条件匹配的所有 xml 项的计数并在终止语句中使用它?

自动生成XSLT - 通用/默认XSLT