我的输入:
我正在使用以下列形式生成RSS Feed的Sharepoint列表:
<?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>
元素似乎定义了一组元素。此外,请注意,并非所有<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>
规则(已更新):
xxx:node-set
是唯一可用的有效扩展功能;这包括用其他语言编写的扩展函数,例如C#或Javascript。<Field2>
元素中的空<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>
属性。)
感谢所有回答的人!
答案 0 :(得分:4)
您可能对此解决方案感兴趣。我使用了文字字段名Field1
虽然Field5
,并且由于您可以访问node-set
,我已将这些名称添加到可以方便修改的变量中。
代码处理description
文本,通过对其中的两个字符串提取每个字段名称的值。第一遍通过选择字段名称后面和文本$rough
之前的文本来创建div
。这将提供类似:</b> Value 1</
(或:</b> Value 1</
)的内容。下一个细化会在$rough
后>
和<
之前将Value 1
中的所有内容转换为normalize-space
。通过在xsl:value-of
元素中使用Field2
,可以从最终值中修剪空格。
如果在目标字符串中找不到分隔符字符串,则XSLT本身通过从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>
<强>输出强>
{{1}}
答案 1 :(得分:0)
她是基于非常好的“提取”模板形式@Borodin的递归解决方案。 利用小优点,这也可以在没有node-set()的情况下工作。
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<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:call-template name="Field" >
<xsl:with-param name="fnr" select="'1'" />
<xsl:with-param name="max_fnr" select="'5'" />
</xsl:call-template>
</Event>
</xsl:template>
<xsl:template name="Field">
<xsl:param name="fnr" />
<xsl:param name="max_fnr" />
<xsl:call-template name="extract">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="field-name" select="concat('Field',$fnr)"/>
</xsl:call-template>
<xsl:if test="$fnr < $max_fnr">
<xsl:call-template name="Field" >
<xsl:with-param name="fnr" select="$fnr+1" />
<xsl:with-param name="max_fnr" select="$max_fnr" />
</xsl:call-template>
</xsl:if>
</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>
将生成以下输出:
<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>
答案 2 :(得分:0)
这是一个带有一些“ifs”的解决方案
如果描述内容始终是“格式良好的XML”(就像在您的示例中那样)和
如果你可以做两个单独的传递(两个xslt处理器调用)。
传递1:为描述内容生成一个disable-output-escaping="yes"
的临时xml文件(简单明了)。
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
传递2:从临时xml文件生成预期输出(现在也很简单):
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<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:call-template name="Field" >
<xsl:with-param name="fnr" select="'1'" />
<xsl:with-param name="max_fnr" select="'5'" />
</xsl:call-template>
</Event>
</xsl:template>
<xsl:template name="Field">
<xsl:param name="fnr" />
<xsl:param name="max_fnr" />
<xsl:element name="Field{$fnr}" >
<xsl:value-of select="description/div[b[text()=concat('Field', $fnr, ':')]]/text()"/>
</xsl:element>
<xsl:if test="$fnr < $max_fnr">
<xsl:call-template name="Field" >
<xsl:with-param name="fnr" select="$fnr+1" />
<xsl:with-param name="max_fnr" select="$max_fnr" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>