通用 Xml 文档操作

发布于 2024-07-29 07:02:21 字数 697 浏览 3 评论 0原文

我们有 xml 文档,其中包含许多标记节点,例如 isProduct、isActive、isMandatory,其中节点文本可能为 True 或 False。

需要操作文档并保持其结构,但将上述节点转换为如下所示的语言表示:

< isProduct >True</ isProduct >   ===>   <Type>Product<Type>
< isProduct >False</ isProduct >  ===>   <Type/>

对于其他标志节点也是如此。

我们正在寻求一种可扩展且可伸缩的解决方案,可以在部署后以最小的摩擦进行配置。

通过可扩展; 我们的意思是还会有更多的病例; 比如代表状态的2个旗帜; 即文档中使用 isEmployee 和 isCustomer 来表示 4 个不同命名的事物。 因此,4 种可能的组合只能转换为一个字符串,如“Employee”、“Customer”、“Customer-Employee”或“None”。

通过可扩展; 我们的意思是,它可以用于处理任何 XML 文档,而无需事先了解其架构,并且对文档大小没有限制。

我们确实知道这可以使用 XSLT 来完成,我们可以编写一个 XSLT 来接受任何文档并生成添加或更新附加节点的相同文档吗?

We have xml documents that contain lots of flagged nodes like isProduct, isActive , isMandatory where the node text may be True or False.

It is needed to manipulate the documents and keep their structure but convert the above nodes into a verbal representation like below:

< isProduct >True</ isProduct >   ===>   <Type>Product<Type>
< isProduct >False</ isProduct >  ===>   <Type/>

And the same for other flag nodes.

We are seeking an extensible and scalable solution that can be configured with minimum friction after deployment.

By extensible; we mean that there will be more cases; like 2 flags that represent a status; i.e. isEmployee and isCustomer is used in the document to represent 4 different named things.; hence the 4 possible combinations should only be translate into one string like "Employee", "Customer", "Customer-Employee" or "None".

By scalable; we mean that it can be used to process any XML document without a prior understanding of its schema and no restriction on the document size.

We do understand that this might be done using XSLT, can we write an XSLT that an accept any document and produce the same document with additional nodes added or update?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

∞梦里开花 2024-08-05 07:02:21

假设这样的输入:

<gizmo>
  <isProduct>True</isProduct>
  <isFoo>False</isFoo>
  <isBar>True</isBar>
</gizmo>

通用方法将是:

<xsl:template match="gizmo">
  <xsl:copy>
    <xsl:apply-templates select="*" />
  </xsl:copy>
</xsl:template>

<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <Type>
    <xsl:if test=". = 'True'">
      <xsl:value-of select="substring-after(local-name(), 'is')" />
    </xsl:if>
  </Type>
</xsl:template>

产生:

<gizmo>
  <Type>Product</Type>
  <Type />
  <Type>Bar</Type>
</gizmo>

更通用的方法使用(大量)修改的身份转换:

<!-- the identity template... well, sort of -->
<xsl:template match="node() | @*">
  <xsl:copy>
    <!-- all element-type children that begin with 'is' -->
    <xsl:variable name="typeNodes"  select="
      *[substring(local-name(), 1, 2) = 'is']
    " />

    <!-- all other children (incl. elements that don't begin with 'this ' -->
    <xsl:variable name="otherNodes" select="
      @* | node()[not(self::*) or self::*[substring(local-name(), 1, 2) != 'is']]
    " />

    <!-- identity transform all the "other" nodes -->
    <xsl:apply-templates select="$otherNodes" />

    <!-- collapse all the "type" nodes into a string -->
    <xsl:if test="$typeNodes">
      <Type>
        <xsl:variable name="typeString">
          <xsl:apply-templates select="$typeNodes" />
        </xsl:variable>
        <xsl:value-of select="substring-after($typeString, '-')" />
      </Type>
    </xsl:if>
  </xsl:copy>
</xsl:template>

<!-- this collapses all the "type" nodes into a string -->
<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <xsl:if test=". = 'True'">
    <xsl:text>-</xsl:text>
    <xsl:value-of select="substring-after(local-name(), 'is')" />
  </xsl:if>
</xsl:template>

<!-- prevent the output of empty text nodes -->
<xsl:template match="text()">
  <xsl:if test="normalize-space() != ''">
    <xsl:value-of select="." />
  </xsl:if>
</xsl:template>

上面的方法接受任何 XML 输入并输出相同的结构,只有名为 的元素。 is*> 被折叠为单个 节点,作为破折号分隔的字符串:

<!-- in -->
<foo>
  <fancyNode />
  <gizmo>
    <isProduct>True</isProduct>
    <isFoo>False</isFoo>
    <isBar>True</isBar>
  </gizmo>
</foo>

<!-- out -->
<foo>
  <fancyNode />
  <gizmo>
    <Type>Product-Bar</Type>
  </gizmo>
</foo>

Assuming an input like this:

<gizmo>
  <isProduct>True</isProduct>
  <isFoo>False</isFoo>
  <isBar>True</isBar>
</gizmo>

The generic approach would be:

<xsl:template match="gizmo">
  <xsl:copy>
    <xsl:apply-templates select="*" />
  </xsl:copy>
</xsl:template>

<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <Type>
    <xsl:if test=". = 'True'">
      <xsl:value-of select="substring-after(local-name(), 'is')" />
    </xsl:if>
  </Type>
</xsl:template>

Which produces:

<gizmo>
  <Type>Product</Type>
  <Type />
  <Type>Bar</Type>
</gizmo>

An even more generalized approach uses a (heavily) modified identity transform:

<!-- the identity template... well, sort of -->
<xsl:template match="node() | @*">
  <xsl:copy>
    <!-- all element-type children that begin with 'is' -->
    <xsl:variable name="typeNodes"  select="
      *[substring(local-name(), 1, 2) = 'is']
    " />

    <!-- all other children (incl. elements that don't begin with 'this ' -->
    <xsl:variable name="otherNodes" select="
      @* | node()[not(self::*) or self::*[substring(local-name(), 1, 2) != 'is']]
    " />

    <!-- identity transform all the "other" nodes -->
    <xsl:apply-templates select="$otherNodes" />

    <!-- collapse all the "type" nodes into a string -->
    <xsl:if test="$typeNodes">
      <Type>
        <xsl:variable name="typeString">
          <xsl:apply-templates select="$typeNodes" />
        </xsl:variable>
        <xsl:value-of select="substring-after($typeString, '-')" />
      </Type>
    </xsl:if>
  </xsl:copy>
</xsl:template>

<!-- this collapses all the "type" nodes into a string -->
<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <xsl:if test=". = 'True'">
    <xsl:text>-</xsl:text>
    <xsl:value-of select="substring-after(local-name(), 'is')" />
  </xsl:if>
</xsl:template>

<!-- prevent the output of empty text nodes -->
<xsl:template match="text()">
  <xsl:if test="normalize-space() != ''">
    <xsl:value-of select="." />
  </xsl:if>
</xsl:template>

The above takes any XML input whatsoever and outputs the same structure, only elements named <is*> are collapsed into a single <Type> node as a dash-delimited string:

<!-- in -->
<foo>
  <fancyNode />
  <gizmo>
    <isProduct>True</isProduct>
    <isFoo>False</isFoo>
    <isBar>True</isBar>
  </gizmo>
</foo>

<!-- out -->
<foo>
  <fancyNode />
  <gizmo>
    <Type>Product-Bar</Type>
  </gizmo>
</foo>
清音悠歌 2024-08-05 07:02:21

以下是基于身份转换的 XSLT 解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="isProduct">
    <xsl:choose>
      <xsl:when test=". = 'True'"><Type>Product</Type></xsl:when>
      <xsl:otherwise><Type/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Here's a solution in XSLT based on the identity transformation:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="isProduct">
    <xsl:choose>
      <xsl:when test=". = 'True'"><Type>Product</Type></xsl:when>
      <xsl:otherwise><Type/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文