【问题标题】:Need to parse XML with html elements需要用 html 元素解析 XML
【发布时间】:2017-10-11 16:51:37
【问题描述】:

我有一个 xml 文件,如下所示:

<?xml version="1.0"?>
<Book>
  <Title>Ulysses</Title>
  <Author>James <b>Joyce</b></Author>
</Book>

我需要使用 Java 将其解析成 pojo 之类的

title="Ulysses"
author="James <b>Joyce</b>"

换句话说,我需要 html 或可能的自定义 xml 标记在解析时保持为纯文本而不是 xml 元素。

我根本无法编辑 XML,但我可以创建一个自定义 xslt 文件来转换 xml。

我有以下 Java 代码使用 xslt 来帮助读取 xml,

TransformerFactory factory = TransformerFactory.newInstance();
    Source stylesheetSource = new StreamSource(new File(stylesheetPathname).getAbsoluteFile());
    Transformer transformer = factory.newTransformer(stylesheetSource);
    Source inputSource = new StreamSource(new File(inputPathname).getAbsoluteFile());
    Result outputResult = new StreamResult(new File(outputPathname).getAbsoluteFile());
    transformer.transform(inputSource, outputResult);

这确实将我的 xslt 应用于写出的文件,但我无法提出正确的 xslt 来执行此操作。我查看了Add CDATA to an xml file,但这对我不起作用。

基本上,我相信我希望文件看起来像

<?xml version="1.0"?>
<Book>
  <Title>Ulysses</Title>
  <Author><![CDATA[James <b>Joyce</b>]]></Author>
</Book>

然后我可以提取 "James &lt;b&gt;Joyce&lt;/b&gt;"。我尝试了这里建议的方法:Add CDATA to an xml file 但这对我不起作用。

我使用了以下 xslt:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

<xsl:template match="Author">
<xsl:copy>
<xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
<xsl:copy-of select="*"/>    
<xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
</xsl:copy>
</xsl:template>

这产生了:

<?xml version="1.0" encoding="UTF-8"?>
  Ulysses
  <Author><![CDATA[
<b>Joyce</b>]]></Author>

你能帮忙吗?我希望完整地写出原始文档,但 CDATA 围绕着 author 元素中的所有内容。 谢谢

【问题讨论】:

  • “它对我不起作用”是什么样的?带有标记的 XML 不是正确的 XML。您可以转义这些魔术字符或包装在 CDATA 中。没有其他选择。

标签: java html xml xslt cdata


【解决方案1】:

使用像 Jsoup 这样的简单 html/xml 解析器不是解决这个问题的更好方法吗? 使用Jsoup 你可以尝试这样的事情:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;

public class Example {

    public static void main(String[] args) {
        String xml = "<?xml version=\"1.0\"?>\n"
                + "<Book>\n"
                + "  <Title>Ulysses</Title>\n"
                + "  <Author>James <b>Joyce</b></Author>\n"
                + "</Book>";
        Document doc = Jsoup.parse(xml, "", Parser.xmlParser());
        doc.outputSettings().prettyPrint(false);
        Elements books = doc.select("Book");
        for(Element e: books){
            Book b = new Book(e.select("Title").html(),e.select("Author").html());
            System.out.println(b.title);
            System.out.println(b.author);
        }
    }
    public static class Book{
        String title;
        String author;

        public Book(String title, String author) {
            this.title = title;
            this.author = author;
        }        
    }
}

【讨论】:

  • 感谢您的回复。在这种情况下,这确实实现了我正在寻找的东西,但是如果我添加更多的 html,它会在由 Jsoup 解析时被更改,我需要的是在不更改的情况下复制元素之间的确切文本。
【解决方案2】:

借助 Saxon 9.8 HE 支持的 XSLT 3.0(适用于 Maven 和 Sourceforge),您可以按如下方式使用 XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:output cdata-section-elements="Author"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="Author">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="serialize(node())"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

至于您的尝试,您基本上需要将用 XSLT 3.0 简洁编写的身份转换模板“实现”为&lt;xsl:mode on-no-match="shallow-copy"/&gt; 作为模板

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

在 XSLT 1.0 中,那些没有被更专业的模板处理的节点(比如 Author 元素的节点)被递归复制。

然后,通过复制选择所有子节点 node() 而不仅仅是元素节点 * 你得到

<xsl:template match="Author">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
<xsl:copy-of select="node()"/>    
<xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
</xsl:copy>
</xsl:template>

【讨论】:

  • 我已经尝试过您的 XSLT 1.0 示例,它现在的行为与预期一样,只是存在一些换行符差异。关于如何解决这个问题的任何想法?我尝试在此评论中添加输入/输出,但显示效果不佳。非常感谢您的回复,非常感谢。
  • 在有或没有xsl:output indent="yes" 的情况下会发生这种情况吗?如果应该保留空格,我会尝试不使用。
  • 我尝试设置 indent="no" 并完全删除它,但结果是一样的。
  • xsltransform.net/naZXpXZ 就元素而言对我来说看起来不错。除了使用设置之外,我不确定如何调整缩进或换行符。你可能想问一个新问题,详细说明你得到的输出和你想要的输出,以及你使用的变压器的详细信息,也许有人有改进方法的想法。
  • 我注意到这种方法不会复制添加 CDATA 的元素的属性,我很感激我的问题中没有说明这一点,但我想知道你是否知道我可以解决这?我尝试将 更改为 但结果是一样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多