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 的复杂场景的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python 或 XSLT 将复杂的 XML 转换为 CSV
在 XSLT 中对记录进行分组时如何避免 O(n^2) 复杂性?