xpath非常适合解析xml文件,但它不适用于cdata标记内的数据:
<![CDATA[ Some Text <p>more text and tags</p>... ]]>
我的解决方案:首先获取xml的内容并删除
"<![CDATA[" and "]]>".
之后我会从xml文件中运行xpath“到达所有内容”。有更好的解决方案吗?如果没有,我怎么能用正则表达式来做呢?
答案 0 :(得分:2)
CDATA标签的原因是它们内部的所有内容都是纯文本,没有什么应该直接解释为XML。您可以将问题中的文档片段替换为
Some Text <p>more text and tags</p>...
(带有前导和尾随空格)。
如果您真的想将其解释为XML,请从文档中提取文本,然后再将其提交给XML解析器。
答案 1 :(得分:1)
我需要完成同样的任务。我用两个xslt解决了它。
请允许我强调,只有当CDATA
为well-formed xml时才会有效。
要完成,让我向你的示例xml添加一个根元素:
<root>
<well-formed-content><![CDATA[ Some Text <p>more text and tags</p>]]>
</well-formed-content>
</root>
图1.-启动xml
在第一个转换步骤中,我已将所有文本节点包装在新引入的xml实体old_text
中:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" version="1.0"
encoding="UTF-8" standalone="yes" />
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*|text()|@*|comment()|processing-instruction()" />
</xsl:copy>
</xsl:template>
<!-- Attribute-nodes and comment-nodes: Pass through without modifying -->
<xsl:template match="@*|comment()|processing-instruction()">
<xsl:copy-of select="." />
</xsl:template>
<!-- Text-nodes: Wrap them in a new node without escaping it. -->
<!-- (note precondition: CDATA should be valid xml. -->
<xsl:template match="text()">
<xsl:element name="old_text">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
图2.-第一个xslt(将CDATA包装在“old_text”元素中)
如果你将这个转换应用到起始xml,这就是你得到的(我没有重新格式化它以避免混淆谁做了什么):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root><old_text>
</old_text><well-formed-content><old_text> Some Text <p>more text and tags</p>
</old_text></well-formed-content><old_text>
</old_text></root>
图3.-转换后的xml(第一步)
您现在需要清理引入的old_text
元素,并重新转义未创建新节点的文本:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" version="1.0"
encoding="UTF-8" standalone="yes" />
<!-- Element-nodes: Process nodes and their children -->
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*|text()|@*|comment()" />
</xsl:copy>
</xsl:template>
<!-- Attribute-nodes and comment-nodes: Pass through without modifying -->
<xsl:template match="@*|comment()">
<xsl:copy-of select="." />
</xsl:template>
<!--
'Wrapper'-node: remove the wrapper element but process its children.
With this matcher, the "old_text" is cleaned, but the originally CDATA
well-formed nodes surface in the resulting xml.
-->
<xsl:template match="old_text">
<xsl:apply-templates select="*|text()" />
</xsl:template>
<!--
Text-nodes: Text here comes from original CDATA and must be now
escaped. Note that the previous rule has extracted all the existing
nodes in the CDATA. -->
<xsl:template match="text()">
<xsl:value-of select="." disable-output-escaping="no" />
</xsl:template>
</xsl:stylesheet>
图4.-第二个xslt(清理过的人工引入元素)
这是最终结果,最初的节点最初在CDATA中扩展到新的xml文件中:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root>
<well-formed-content> Some Text <p>more text and tags</p>
</well-formed-content>
</root>
图5.-最终xml
如果您的CDATA包含xml不支持的html字符实体(请查看此wikipedia article about character entities处的示例),则需要将这些引用添加到中间xml。让我用一个例子来说明这一点:
<root>
<well-formed-content>
<![CDATA[ Some Text <p>more text and tags</p>,
now with a non-breaking-space before the stop: .]]>
</well-formed-content>
</root>
图6.-向图1中的xml添加了字符实体
来自图2 的原始xslt会将xml转换为:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root><old_text>
</old_text><well-formed-content><old_text>
Some Text <p>more text and tags</p>,
now with a non-breaking-space before the stop: .
</old_text></well-formed-content><old_text>
</old_text></root>
图7.-第一次尝试转换图6中的xml的结果(格式不正确!)
此文件存在的问题是格式不正确,因此无法使用XSLT处理器进一步处理:
The entity "nbsp" was referenced, but not declared. XML checking finished.
图8-图7中xml格式良好检查的结果
这种解决方法可以解决问题(match="/"
模板添加
实体):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" version="1.0"
encoding="UTF-8" standalone="yes" />
<!-- Add an html entity to the xml character entities declaration. -->
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE root
[
<!ENTITY nbsp " ">
]>
]]>
</xsl:text>
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*|text()|@*|comment()|processing-instruction()" />
</xsl:copy>
</xsl:template>
<!-- Attribute-nodes and comment-nodes: Pass through without modifying -->
<xsl:template match="@*|comment()|processing-instruction()">
<xsl:copy-of select="." />
</xsl:template>
<!-- Text-nodes: Wrap them in a new node without escaping it. -->
<!-- (note precondition: CDATA should be valid xml. -->
<xsl:template match="text()">
<xsl:element name="old_text">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
图9.- xslt创建实体声明
现在,将此xslt应用于图6 源xml后,这是中间的xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE root
[
<!ENTITY nbsp " ">
]>
<root><old_text>
</old_text><well-formed-content><old_text>
Some Text <p>more text and tags</p>,
now with a non-breaking-space before the stop: .
</old_text></well-formed-content><old_text>
</old_text></root>
图10.-中级xml(图3中的xml加实体声明)
您可以使用图4 中的xslt转换来生成最终的xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root>
<well-formed-content>
Some Text <p>more text and tags</p>,
now with a non-breaking-space before the stop: .
</well-formed-content>
</root>
图11.-带有html entites的最终xml转换为UTF-8
对于这些示例,我使用了NetBeans 7.1.2内置XSLT处理器(com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl - default JRE XSLT processor
)
免责声明:我不是XML专家。我觉得这应该更容易......
答案 2 :(得分:1)
要剥离CDATA并将标记保留为标记,可以使用XSLT。
鉴于此XML输入:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<child>Here is some text.</child>
<child><![CDATA[Here is more text <p>with tags</p>.]]></child>
</root>
使用此XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*" />
<xsl:value-of select="text()" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
将返回以下XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>Here is some text.</child>
<child>Here is more text <p>with tags</p>.</child>
</root>
(在oXygen 12.2中用Saxon HE 9.3.0.5测试)
然后您可以使用xPath提取p
元素的内容:
/root/child/p
答案 3 :(得分:0)
您绝对可以使用正则表达式从xml中删除cdata,从xml中删除所需的内容。
例如:
String s = "<sn><![CDATA[poctest]]></sn>";
s = s.replaceAll("!\\[CDATA", "");
s = s.replaceAll("]]", "");
s = s.replaceAll("\\[", "");
结果将是:
<sn><poctest></sn>
请检查,如果这样可以解决您的问题。
答案 4 :(得分:0)
试试这个:
public static removeCDATA (String text) {
String resultString = "";
Pattern regex = Pattern.compile("(?<!(<!\\[CDATA\\[))|((.*)\\w+\\W)");
Matcher regexMatcher = regex.matcher(text);
while (regexMatcher.find()) {
resultString += regexMatcher.group();
}
return resultString;
}
当我使用您的测试输入<![CDATA[ Some Text <p>more text and tags</p>... ]]>
方法调用此方法时返回Some Text <p>more text and tags</p>
但我认为没有正则表达式的方法会更可靠。像这样:
public static removeCDATA (String text) {
s = s.trim();
if (s.startsWith("<![CDATA[")) {
s = s.substring(9);
int i = s.indexOf("]]>");
if (i == -1) throw new IllegalStateException("argument starts with <![CDATA[ but cannot find pairing ]]>");
s = s.substring(0, i);
}
return s;
}