这个问题最近一直困扰着我,我似乎无法找到可能的解决方案。
我正在处理一个Web服务器,它接收一个XML文档来进行一些处理。服务器的解析器与&,&#;;,#34;,<,>有问题。我知道这很糟糕,我没有在该服务器上实现xml解析器。但在等待补丁之前我需要规避。
现在,在将我的XML文档上传到此服务器之前,我需要解析它并转义xml特殊字符。我目前正在使用DOM。问题是,如果我遍历TEXT_NODES并用转义版本替换所有特殊字符,当我保存此文档时,
d'ex
我得到d'ex
但我需要d'ex
这是有道理的,因为,DOM逃脱"&"。但显然这不是我需要的。
因此,如果DOM已经能够将"&"
转发到"&"
,我怎样才能让它从"
转换为"
等其他字符?
如果它不能,我怎样才能在已经解析和转义的文本中保存它们,而不必在保存时重新转义它们?
这就是我如何逃避使用apache StringEscapeUtils class:
的特殊字符public String xMLTransform() throws Exception
{
String xmlfile = FileUtils.readFileToString(new File(filepath));
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new InputSource(new StringReader(xmlfile.trim().replaceFirst("^([\\W]+)<", "<"))));
NodeList nodeList = doc.getElementsByTagName("*");
for (int i = 0; i < nodeList.getLength(); i++) {
Node currentNode = nodeList.item(i);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Node child = currentNode.getFirstChild();
while(child != null) {
if (child.getNodeType() == Node.TEXT_NODE) {
child.setNodeValue(StringEscapeUtils.escapeXml10(child.getNodeValue()));
//Escaping works here. But when saving the final document, the "&" used in escaping gets escaped as well by DOM.
}
child = child.getNextSibling();
}
}
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
FileOutputStream fop = null;
File file;
file = File.createTempFile("escapedXML"+UUID.randomUUID(), ".xml");
fop = new FileOutputStream(file);
String xmlString = writer.toString();
byte[] contentInBytes = xmlString.getBytes();
fop.write(contentInBytes);
fop.flush();
fop.close();
return file.getPath();
}
答案 0 :(得分:3)
我认为您正在寻找的解决方案是一个自定义的XSLT解析器,您可以为其他HTML转义进行配置。
我无法说某些如何配置xslt文件以执行您想要的操作,但我相信它可以完成。我已经删除了下面的基本Java设置:
@Test
public void testXSLTTransforms () throws Exception {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element el = doc.createElement("Container");
doc.appendChild(el);
Text e = doc.createTextNode("Character");
el.appendChild(e);
//e.setNodeValue("\'");
//e.setNodeValue("\"");
e.setNodeValue("&");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(System.out);
//This prints the original document to the command line.
transformer.transform(source, result);
InputStream xsltStream = getClass().getResourceAsStream("/characterswap.xslt");
Source xslt = new StreamSource(xsltStream);
transformer = transformerFactory.newTransformer(xslt);
//This one is the one you'd pipe to a file
transformer.transform(source, result);
}
我有一个简单的XSLT,用于概念验证,显示您提到的默认字符编码:
characterswap.xslt
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:text> 
 Original VALUE : </xsl:text>
<xsl:copy-of select="."/>
<xsl:text> 
 OUTPUT ESCAPING DISABLED : </xsl:text>
<xsl:value-of select="." disable-output-escaping="yes"/>
<xsl:text> 
 OUTPUT ESCAPING ENABLED : </xsl:text>
<xsl:value-of select="." disable-output-escaping="no"/>
</xsl:template>
</xsl:stylesheet>
控制台非常基本:
<?xml version="1.0" encoding="UTF-8"?>
<Container>&</Container>
Original VALUE : <Container>&</Container>
OUTPUT ESCAPING DISABLED : &
OUTPUT ESCAPING ENABLED : &
您可以从XSLT执行中获取活动节点并执行特定的字符替换。我可以找到多个例子,但是我很难让他们在我的环境中工作。
XSLT string replace 是一个很好的起点。
这是关于我对XSLT的了解程度,我希望它可以帮助您解决问题。
祝你好运。
我正在考虑进一步,解决方案可能不仅仅是XSLT。根据您的描述,我的印象是,您需要寻找一整套 html编码,而不是 xml10编码。
沿着这些方向,如果我们采用您当前的节点文本转换:
if (child.getNodeType() == Node.TEXT_NODE) {
child.setNodeValue(StringEscapeUtils.escapeXml10(child.getNodeValue()));
}
明确指望我们想要HTML编码:
if (child.getNodeType() == Node.TEXT_NODE) {
//Capture the current node value
String nodeValue = child.getNodeValue();
//Decode for XML10 to remove existing escapes
String decodedNode = StringEscapeUtils.unescapeXml10(nodeValue);
//Then Re-encode for HTML (3/4/5)
String fullyEncodedHTML = StringEscapeUtils.escapeHtml3(decodedNode);
//String fullyEncodedHTML = StringEscapeUtils.escapeHtml4(decodedNode);
//String fullyEncodedHTML = StringEscapeUtils.escapeHtml5(decodedNode);
//Then place the fully-encoded HTML back to the node
child.setNodeValue(fullyEncodedHTML);
}
我认为xml现在将完全用所有的编码 HTML逃脱了你想要的。
现在将它与XSLT结合用于输出转义(从上面),并且当写出文件时文档将不会进行任何进一步的转换。
我喜欢这个解决方案,因为它限制了XSLT文件中的逻辑。您无需管理整个String查找/替换,而只需确保复制整个节点并复制 text()并禁用输出转义。
从理论上讲,这似乎可以实现我对你的目标的理解。
再次警告我使用XSLT是弱的,所以示例xslt文件可能 还需要一些调整。该解决方案减少了未知的工作 数量,在我看来。
答案 1 :(得分:1)
我已经看到人们使用正则表达式来做类似的事情
从(Replace special character with an escape preceded special character in Java)
复制 String newSearch = search.replaceAll("(?=[]\\[+&|!(){}^\"~*?:\\\\-])", "\\\\");
那个糟糕的正则表达式是&#34;展望未来&#34; - 非捕获断言,以下char匹配某些东西 - 在本例中为字符类。
请注意你不需要在角色类中逃脱角色,除了a](即使是减号也不需要在第一个或最后一个时进行转义)。
\\\\
是你编写正则表达式文字的代码\(对java进行一次转义,对正则表达式进行一次转义)
这是对这项工作的考验:
public static void main(String[] args) {
String search = "code:xy";
String newSearch = search.replaceAll("(?=[]\\[+&|!(){}^\"~*?:\\\\-])", "\\\\");
System.out.println(newSearch);
}
输出:
code\:xy
答案 2 :(得分:1)
这与这个问题密切相关(how to Download a XML file from a URL by Escaping Special Characters like < > $amp; etc?)。
这篇文章有类似的情况,代码下载带解析/转义内容的XML。
据我所知,你读取文件,解析它并转义字符。在保存期间,XML会被转义&#34;再次。虽然您可以使用DOM来检查格式良好的XML或模式,但要转义的基于文件的操作可以帮助您转义XML和HTML特殊字符。帖子中的代码示例指的是使用IOUtils和StringUtils来完成它。希望这可以帮助 !
答案 3 :(得分:0)
我会在这里使用StringEscapeUtils.escapeXml10()...详细信息。 https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringEscapeUtils.html#ESCAPE_XML10