【问题标题】:Exclude specific elements from XML using XPath or XSLT使用 XPath 或 XSLT 从 XML 中排除特定元素
【发布时间】:2019-08-18 11:23:51
【问题描述】:

考虑以下 XML:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>

<book category="cooking">
  <title lang="en">Everyday Italian</title>
  <author>Giada De Laurentiis</author>
  <price>30.00</price>
  <contents>jjjadLKjlkdasndlakjd...</contents>
</book>

<book category="children">
  <title lang="en">Harry Potter</title>
  <author>J K. Rowling</author>
  <price>29.99</price>
</book>

<book category="web" cover="paperback">
  <title lang="en">Learning XML</title>
  <author>Erik T. Ray</author>
  <price>39.95</price>
  <contents>jjjadLKjlkdasndlakjd...</contents>
</book>

</bookstore>

目标是排除“内容”元素并按原样获取生成的 XML。

我尝试了轴和其他运算符。但是,使用 XPath 似乎无法实现这一点。如果我错了,请纠正我。

如果 XPath 解决方案不可行,下面的 XSLT 可以工作吗?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8"/>

  <!-- For each element, create a new element with the same local-name (no namespace) -->
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:copy-of select="@*"/> 
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="*[not(self::company)]"/>
  </xsl:template>

</xsl:stylesheet>

【问题讨论】:

  • XPath 只能从源文档中选择元素,它不能以任何方式修改它们(例如,通过删除一些子元素)

标签: c# xslt xpath


【解决方案1】:

只需将 XSLT 的第二个模板更改为空模板即可:

<xsl:template match="contents" />

此外,如果您想保留命名空间(以保持 XML 的其余部分“原样”),您可以简单地使用身份模板:

<!-- identity template -->
<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template> 

【讨论】:

  • 谢谢,我试试这个。想一想,这里的“内容”元素是一个 3 到 4 MB 的 PDF 文档,采用 base 64 字符串格式。可以使用正则表达式来选择 XML 的所需部分或用空字符串替换“内容”标签。您认为哪种方法更好? XSLT 还是正则表达式?
  • 使用 RegEx 过滤 XML 文件被认为是不好的做法,因此 XSLT 是首选语言。如果您需要使用 RegEx 过滤 &lt;contents&gt; 元素的 base64 内容,则必须使用 XSLT-2.0 或更高版本(查找 fn:replace 函数),但 XSLT-2.0 不是 .Net 框架的一部分,并且所以在这种情况下,您需要一个外部 XSLT-2.0 或 XSLT-3.0 处理器。
【解决方案2】:

如果要排除contents元素,需要使用:

<xsl:apply-templates select="*[not(self::contents)]"/>

而不是你的:

<xsl:apply-templates select="*[not(self::company)]"/>

此外,由于contentsbook 的子节点,因此您需要在匹配book 元素而不是/ 根节点的模板中执行此操作。而且您还想复制父 book 及其属性 - 那就是:

<xsl:template match="book">
  <xsl:copy>
    <xsl:apply-templates select="@* | *[not(self::contents)]"/>
  </xsl:copy>
</xsl:template>

或者 - 如果您需要删除源名称空间(您的示例没有?):

<xsl:template match="book">
  <xsl:element name="{local-name()}">
    <xsl:apply-templates select="@* | *[not(self::contents)]"/>
  </xsl:element>
</xsl:template>

【讨论】:

  • 谢谢,“公司”是错字。它应该是“内容”。
【解决方案3】:

使用 Xml Linq:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            List<XElement> contents = doc.Descendants("contents").ToList();
            for (int i = contents.Count - 1; i >= 0; i--)
            {
                contents[i].Remove();
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多