1

我需要将 XML 转换成类似于英文句子的东西。例如以下 XML:

<event>
<criteria>
    <and>A</and>
    <and>B</and>
    <and>
        <or>
            <and>C</and>
            <and>D</and>
        </or>
        <or>E</or>
    </and>
</criteria>
</event>

必须变成类似的东西:

To meet the criteria event must have A and B and either C and D or E.

这是一个示例,但“and”和“or”条件可以进一步嵌套。

规则似乎是:

  • 如果一个元素没有后续的兄弟姐妹或孩子,那么什么都不会输出,你就完成了。
  • 如果“and”或“or”有一个没有子代的后续兄弟,则输出后续兄弟的类型(“and”或“or”)。(例如,A和B;C和D;D或E)
  • 如果“and”后面有一个带有“or”子节点的“and”兄弟,则输出“and either”(例如,and either C)。
  • 不输出没有文本的元素。

我尝试了几种方法来生成此输出,但都没有成功。一个问题是没有得到正确的递归。我见过很多嵌套一个元素的 xslt 处理示例(例如,Item 可以由由其他 Item 组成的其他 Item 组成,等等),但是没有像“and”和“or”这样的两个元素的示例" 可以是兄弟姐妹和/或相互嵌套。我尝试使用 xsl:template match= "and | or" 然后测试 "and" 或 "or",但我要么没有降到叶级别,要么以错误的顺序出现。

我想知道是否有人可以为我指出正确的方向来处理这样的结构,和/或是否有人可以提出更好的结构来表示“布尔”句子。由于 XML 尚未最终确定,如果可以使处理更容易,可以对其进行修改。

注意:我使用的是 Saxon 9,可以使用 xslt 2.0 解决方案。

更多信息:

再次感谢@g-ken-holman。我喜欢建议的自上而下的方法,但我遇到了一些问题。我不确定为什么在肯的例子中和/或序列被更改为或/和。和/或顺序似乎是正确的。无论如何,我运行了这个例子并且它有效。但是,我总共收到了 5 个案例。它适用于前两个简单的情况,所有情况都是和或或,以及情况 5,即上面的情况。但是案例 3 和 4 没有用。这是 XML 和结果。

 <event>
<example>3</example>
<criteria>
    <or>
        <op>A</op>
        <op>B</op>
    </or>
    <and>
        <op>C</op>
    </and>
</criteria>
</event>

Result: To meet the criteria, event must have either A or B C
Expected: To meet the criteria, event must have either A or B and C

示例 4:

<event>
  <example>4</example>
  <criteria>
<and>
    <op>A</op>
    <op>B</op>
</and>
<and>
    <or>
        <op>C</op>
        <op>D</op>
        <op>E</op>
    </or>
</and>
  </criteria>
</event>

结果:要满足条件,事件必须具有 A 和 BC 或 D 或 E 预期:要满足条件,事件必须具有 A 和 B 以及 C 或 D 或 E

我认为原因是 and/or or 仅在有多个 (position()>1) 测试时才输出。但这不会涵盖所有情况。也许如果节点数的位置()> 1 = 1?

如果这会使它更容易,可以添加“任何一个”元素。

回答注意事项:

这对于评论部分来说太长了,所以我在这里添加它。我相信@Ken 已经提供了答案,并且他建议的第二种方法是最好的。

如果我了解处理。我们正在匹配文档中的所有节点。我们匹配“事件”并首先执行,因为它嵌套在其他节点之外。然后,如果遇到“and”节点,我们会在“and”上得到一个匹配,然后我们迭代(for-each)该级别的所有“and”兄弟节点。我们不会为第一个节点输出单词“and”,因为测试“position() > 1”失败。我们总是使用 xls:text 输出一个空格。接下来我们从当前(上下文)节点()应用模板。这开始让我们沿着树向下走,因为我们现在只匹配“and”的子节点。如果我们接下来匹配一个“and”,我们将重复我们到目前为止所做的事情。如果我们接下来匹配一个“或”,我们会执行 match="or" 模板,这与“and”几乎相同 除了它输出单词“或”。但是,有两个可能的模板匹配“或”和 1]" priority="1">。priority="1" 将该匹配项的优先级设置为高于另一个“或”匹配项,因为除非指定了优先级, 匹配的默认优先级为 0.5. 因此如果当前 "or" 节点有 2 个子节点 (or[count(*) > 1]), 我们输出 "either" 然后调用这将允许较低优先级 "or"匹配运行。匹配的默认优先级为 0.5。因此,如果当前“or”节点有 2 个子节点(or[count(*) > 1]),我们输出“either”,然后调用将允许较低优先级的“or”匹配运行。匹配的默认优先级为 0.5。因此,如果当前“or”节点有 2 个子节点(or[count(*) > 1]),我们输出“either”,然后调用将允许较低优先级的“or”匹配运行。

我认为这是正确的,但我有一个问题。操作数的文本如何输出?

4

2 回答 2

1

我建议您始终“自上而下”地处理您的数据,而不是尝试处理兄弟姐妹。

下面是一个解决方案:

t:\ftemp>type boolean1.xml 
<event>
<criteria>
    <and>A</and>
    <and>B</and>
    <and>
        <or>
            <and>C</and>
            <and>D</and>
        </or>
        <or>E</or>
    </and>
</criteria>
</event>
t:\ftemp>call xslt2 boolean1.xml boolean1.xsl 

To meet the criteria, event must have A and B and  either  C and D or E
t:\ftemp>type boolean1.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">

<xsl:output method="text"/>

<!--eat white-space-->
<xsl:template match="text()[not(normalize-space())]"/>

<!--start result-->
<xsl:template match="event">
To meet the criteria, event must have<xsl:apply-templates/>
</xsl:template>

<!--handle conjunction-->
<xsl:template match="*[child::and]">
  <xsl:for-each select="child::and">
    <xsl:if test="position()>1"> and</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--handle alternation-->
<xsl:template match="*[child::or]">
  <xsl:for-each select="child::or">
    <xsl:if test="position()>1"> or</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--special grammar case for alternation between 2 operands-->
<xsl:template match="*[count(child::or) = 2]" priority="1">
  <xsl:text> either</xsl:text>
  <xsl:next-match/>
</xsl:template>

<!--don't allow a mixture-->
<xsl:template match="*[child::and and child::or]" priority="2">
  <xsl:message terminate="yes">
    <xsl:text>A mixture of ands and ors is not allowed.</xsl:text>
  </xsl:message>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done! 

至于更改 XML 的建议,我建议使用不允许意外组合的结构,例如“当 ands 和 ors 都是兄弟时该怎么办”。考虑以下:

t:\ftemp>type boolean2.xml 
<event>
<criteria>
  <and>
    <op>A</op>
    <op>B</op>
    <or>
      <and>
        <op>C</op>
        <op>D</op>
      </and>
      <op>E</op>
    </or>
  </and>
</criteria>
</event>
t:\ftemp>call xslt2 boolean2.xml boolean2.xsl 

To meet the criteria, event must have A and B and  either  C and D or E
t:\ftemp>type boolean2.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">

<xsl:output method="text"/>

<!--eat white-space-->
<xsl:template match="text()[not(normalize-space())]"/>

<!--start result-->
<xsl:template match="event">
To meet the criteria, event must have<xsl:apply-templates/>
</xsl:template>

<!--handle conjunction-->
<xsl:template match="and">
  <xsl:for-each select="*">
    <xsl:if test="position()>1"> and</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--handle alternation-->
<xsl:template match="or">
  <xsl:for-each select="*">
    <xsl:if test="position()>1"> or</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--special grammar case for alternation between 2 operands-->
<xsl:template match="or[count(*) = 2]" priority="1">
  <xsl:text> either</xsl:text>
  <xsl:next-match/>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem Done! 

在第二种方法中,“动作”由元素触发,而不是由子操作数元素触发。我认为这会更直接。

请注意,对于英语读者来说,当在某处没有标点符号的情况下深度嵌套 ands 和 ors 时,可能会遇到一些语法挑战。

于 2013-09-04T23:10:42.293 回答
0

此替代答案具有相同的样式表逻辑(​​唯一的变化是示例编号的说明),但发布是为了解决示例 3 和 4 的已编辑问题。

你在哪里:

<event>
<example>3</example>
<criteria>
    <or>
        <op>A</op>
        <op>B</op>
    </or>
    <and>
        <op>C</op>
    </and>
</criteria>
</event>

我将编写与以下内容相同的内容,它使用我的原始逻辑为您提供所需的结果:

t:\ftemp>type boolean3.xml
<event>
<example>3</example>
<criteria>
  <and>
    <or>
        <op>A</op>
        <op>B</op>
    </or>
    <op>C</op>
  </and>
</criteria>
</event>
t:\ftemp>xslt2 boolean3.xml boolean2.xsl
3 To meet the criteria, event must have  either A or B and C

类似地,例如 4,您有:

<event>
  <example>4</example>
  <criteria>
<and>
    <op>A</op>
    <op>B</op>
</and>
<and>
    <or>
        <op>C</op>
        <op>D</op>
        <op>E</op>
    </or>
</and>
  </criteria>
</event>

我会这样写:

t:\ftemp>type boolean4.xml
<event>
  <example>4</example>
  <criteria>
<and>
    <op>A</op>
    <op>B</op>
    <or>
        <op>C</op>
        <op>D</op>
        <op>E</op>
    </or>
</and>
  </criteria>
</event>
t:\ftemp>xslt2 boolean4.xml boolean2.xsl
4 To meet the criteria, event must have A and B and  C or D or E

在我的代码中,我只在有两个操作数时才使用“任何一个”这个词or……我想当有两个以上的操作数时它也可以工作,所以你可以将它添加到or处理逻辑中。

下面是为容纳示例编号而修改的样式表:

t:\ftemp>type boolean2.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">

<xsl:output method="text"/>

<!--eat white-space-->
<xsl:template match="text()[not(normalize-space())]"/>

<!--start result-->
<xsl:template match="event">
  <xsl:value-of select="example"/>
  <xsl:text> To meet the criteria, event must have</xsl:text>
  <xsl:apply-templates select="criteria"/>
</xsl:template>

<!--handle conjunction-->
<xsl:template match="and">
  <xsl:for-each select="*">
    <xsl:if test="position()>1"> and</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--handle alternation-->
<xsl:template match="or">
  <xsl:for-each select="*">
    <xsl:if test="position()>1"> or</xsl:if>
    <xsl:text> </xsl:text>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</xsl:template>

<!--special grammar case for alternation between 2 operands-->
<xsl:template match="or[count(*) = 2]" priority="1">
  <xsl:text> either</xsl:text>
  <xsl:next-match/>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>

因此,这完全取决于您如何编写 XML。检查我是如何将您所做的事情重新写入操作数的工作原理的,并询问您是否需要更多说明。

于 2013-09-05T19:30:10.780 回答