我的意见:
我正在使用一个 Sharepoint 列表,它以以下形式生成 RSS 源:
<?xml version="1.0"?>
<rss>
<channel>
<!-- Irrelevant Fields -->
<item>
<title type="text">Title</title>
<description type="html">
<div><b>Field1:</b> Value 1</div>
<div><b>Field2:</b> Value 2</div>
<div><b>Field3:</b> Value 3</div>
<div><b>Field4:</b> Value 4</div>
<div><b>Field5:</b> Value 5</div>
</description>
</item>
<item>
<title type="text">Title</title>
<description type="html">
<div><b>Field1:</b> Value 1</div>
<div><b>Field3:</b> Value 3</div>
<div><b>Field4:</b> Value 4</div>
<div><b>Field5:</b> Value 5</div>
</description>
</item>
<item>
<title type="text">Title</title>
<description type="html">
<div><b>Field1:</b> Value 1</div>
<div><b>Field2:</b> Value 2</div>
<div><b>Field3:</b> Value 3</div>
<div><b>Field4:</b> Value 4</div>
<div><b>Field5:</b> Value 5</div>
</description>
</item>
<!-- More <item> elements -->
</channel>
</rss>
请注意 <description>
element 似乎定义了一组元素。此外,请注意并非所有 <description>
元素包含“Field2”的标记。
我需要什么:
我需要以下形式的 XML:
<?xml version="1.0"?>
<Events>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2/>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
</Events>
规则(更新):
- 这需要是 XSLT 1.0 解决方案。
-
xxx:node-set
是我可用的唯一有效的扩展函数;这包括用其他语言(例如 C# 或 Javascript)编写的扩展函数。 - 如果任何字段的信息丢失,则应输出空白元素。请注意我所需的输出为空
<Field2>
第二个内的 child<Event>
元素。 - 我们不能假设字段名称本身遵循任何特定模式;他们也可能是
<PeanutButter>
,<Jelly>
等等
到目前为止我所拥有的:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<Events>
<xsl:apply-templates select="*/item"/>
</Events>
</xsl:template>
<xsl:template match="item[contains(description, 'Field2')]">
<Event>
<xsl:variable name="vElements">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="description"/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</xsl:variable>
<Category>
<xsl:value-of select="title"/>
</Category>
<xsl:apply-templates
select="exsl:node-set($vElements)/*[normalize-space()]" mode="token"/>
</Event>
</xsl:template>
<!-- NOTE HOW THIS TEMPLATE IS NEARLY IDENTICAL TO THE LAST ONE,
MINUS THE BLANK <Field2>; THAT'S NOT VERY ELEGANT. -->
<xsl:template match="item[not(contains(description, 'Field2'))]">
<Event>
<xsl:variable name="vElements">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="description"/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</xsl:variable>
<Category>
<xsl:value-of select="title"/>
</Category>
<xsl:apply-templates
select="exsl:node-set($vElements)/*[normalize-space()]" mode="token"/>
<Field2/>
</Event>
</xsl:template>
<xsl:template match="*" mode="token">
<xsl:element
name="{substring-after(
substring-before(normalize-space(), ':'),
'<div><b>')}">
<xsl:value-of
select="substring-before(
substring-after(., ':</b> '),
'</div>')"/>
</xsl:element>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:choose>
<xsl:when test="contains($text,$delimiter)">
<xsl:element name="token">
<xsl:value-of select="substring-before($text,$delimiter)"/>
</xsl:element>
<xsl:call-template name="tokenize">
<xsl:with-param
name="text"
select="substring-after($text,$delimiter)"/>
<xsl:with-param
name="delimiter"
select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$text">
<xsl:element name="token">
<xsl:value-of select="$text"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
...产生:
<?xml version="1.0"?>
<Events>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
<Field2/>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
</Events>
我的解决方案有两个主要问题:
- 感觉很笨重;有重复的代码,看起来有点笨拙。我认为可以进行一些优化?
- 请注意,它输出空
<Field2>
元素的顺序不正确并将它们放置在底部。我想这很容易解决,但我所有的解决方案看起来都很愚蠢,因此不包括在内。 :)
准备,出发,出发!
非常感谢您提供更优雅的解决方案(或者至少修复上述问题 2 的解决方案)。谢谢!
结论
根据@Borodin 在他自己的解决方案中所做的观察,我决定采用以下方法:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vFieldNames">
<name oldName="Field1" newName="fieldA" />
<name oldName="Field2" newName="fieldB" />
<name oldName="Field3" newName="fieldC" />
<name oldName="Field4" newName="fieldD" />
<name oldName="Field5" newName="fieldE" />
</xsl:variable>
<xsl:template match="/">
<events>
<xsl:apply-templates select="*/*/item" />
</events>
</xsl:template>
<xsl:template match="item">
<event>
<category>
<xsl:value-of select="title" />
</category>
<xsl:apply-templates select="exsl:node-set($vFieldNames)/*">
<xsl:with-param
name="pDescriptionText"
select="current()/description" />
</xsl:apply-templates>
</event>
</xsl:template>
<xsl:template match="name">
<xsl:param name="pDescriptionText" />
<xsl:variable
name="vRough"
select="substring-before(
substring-after($pDescriptionText, @oldName),
'div')"/>
<xsl:variable
name="vValue"
select="substring-before(
substring-after($vRough, '>'),
'<')"/>
<xsl:element name="{@newName}">
<xsl:value-of select="normalize-space($vValue)" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
此解决方案添加了一个额外的层:它允许我很好地更改字段名称(通过每个 oldName
元素上的 newName
和 <name>
属性)。
感谢所有回答的人!
最佳答案
您可能对此解决方案感兴趣。我使用了文字字段名称 Field1
虽然Field5
并且,因为您可以访问 node-set
,我把这些名字添加到了一个可以方便修改的变量中。
代码处理 description
文本,通过咬两下来提取每个字段名称的值。第一遍创建 $rough
通过选择字段名称后面和文本之前的文本 div
。这将给出类似 :</b> Value 1</
的内容(或:</b> Value 1</
)。下一个改进将 $rough
中的所有内容都包含在内。之后>
和之前<
,给予Value 1
。使用 normalize-space
从此最终值中修剪空格。在 xsl:value-of
元素。
XSLT 本身会处理缺失的 Field2
(或任何字段)通过从 substring-before
返回空字符串如果在目标字符串中找不到分隔符字符串。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="names">
<name>Field1</name>
<name>Field2</name>
<name>Field3</name>
<name>Field4</name>
<name>Field5</name>
</xsl:variable>
<xsl:template match="/">
<Events>
<xsl:apply-templates select="rss/channel/item"/>
</Events>
</xsl:template>
<xsl:template match="item">
<xsl:variable name="description" select="description"/>
<Event>
<Category>
<xsl:value-of select="title"/>
</Category>
<xsl:for-each select="ext:node-set($names)/name">
<xsl:call-template name="extract">
<xsl:with-param name="text" select="$description"/>
<xsl:with-param name="field-name" select="."/>
</xsl:call-template>
<xsl:variable name="field-name" select="."/>
</xsl:for-each>
</Event>
</xsl:template>
<xsl:template name="extract">
<xsl:param name="text"/>
<xsl:param name="field-name"/>
<xsl:variable name="rough" select="substring-before(substring-after($text, $field-name), 'div')"/>
<xsl:variable name="value" select="substring-before(substring-after($rough, '>'), '<')"/>
<xsl:element name="{$field-name}">
<xsl:value-of select="normalize-space($value)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
输出
<?xml version="1.0" encoding="utf-8"?>
<Events>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2/>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
<Event>
<Category>Title</Category>
<Field1>Value 1</Field1>
<Field2>Value 2</Field2>
<Field3>Value 3</Field3>
<Field4>Value 4</Field4>
<Field5>Value 5</Field5>
</Event>
</Events>
关于xml - 标记化 XSLT 的优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16390657/