繁体   English   中英

如何使用DOM转义特殊字符

[英]How can i escape special characters with using DOM

这个问题最近困扰着我很多,我似乎找不到可能的解决方案。

我正在处理一个接收XML文档进行某些处理的Web服务器。 服务器的解析器出现&,',“,<,>问题。我知道这很不好,我没有在该服务器上实现xml解析器。但是在等待补丁之前,我需要规避。

现在,在将我的XML文档上载到此服务器之前,我需要解析它并转义xml特殊字符。 我目前正在使用DOM。 问题是,如果我遍历TEXT_NODES并将所有特殊字符替换为它们的转义版本,那么在保存此文档时,

对于d'ex我得到d&amp;apos;ex但我需要d&apos;ex

这是有道理的,因为DOM会转义“&”。 但这显然不是我所需要的。

因此,如果DOM已经能够将"&"转义为"&amp;" 如何使它转义其他字符,例如" to &quot;

如果不能,如何保存已解析和转义的文本到其节点中,而不必在保存时重新转义它们?

这就是我转义我使用的Apache StringEscapeUtils类中的特殊字符的方式:

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();


      }

我认为您正在寻找的解决方案是定制的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用于概念验证,它显示了您提到的默认字符编码:

characterwap.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> &#xa;  Original VALUE :  </xsl:text>
     <xsl:copy-of select="."/>
     <xsl:text> &#xa;  OUTPUT ESCAPING DISABLED :  </xsl:text>
      <xsl:value-of select="." disable-output-escaping="yes"/>
      <xsl:text> &#xa;  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>&amp;</Container>

  Original VALUE :  <Container>&amp;</Container> 
  OUTPUT ESCAPING DISABLED :  & 
  OUTPUT ESCAPING ENABLED :  &amp;

您可以从XSLT执行中获取活动节点并执行特定的字符替换。 我可以找到多个示例,但是我很难让它们在我的环境中工作。

XSLT字符串替换是一个不错的起点。

这与我对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);
}

我认为现在可以用您想要的所有HTML转义对xml进行完全编码。

现在,将其与XSLT结合使用以进行转义(从上面),并且将文档写到文件时,文档将不会进行任何进一步的转换。

我喜欢这种解决方案,因为它限制了XSLT文件中包含的逻辑。 无需管理整个String查找/替换,您只需要确保复制了整个节点并复制了禁用输出转义的text()即可

从理论上讲,这似乎可以满足我对您目标的理解。

需要再次说明的是,我对XSLT不满意,因此示例xslt文件可能仍需要进行一些调整。 我认为这种解决方案减少了未知的工作量。

我见过人们使用正则表达式来做类似的事情

从复制( 用Java中的特殊字符替换为特殊字符

String newSearch = search.replaceAll("(?=[]\\\\[+&|!(){}^\\"~*?:\\\\\\\\-])", "\\\\\\\\");

那个古怪的正则表达式是一个“前瞻性”-一个无法捕获的断言,即后面的字符与某些字符匹配-在这种情况下为字符类。

请注意,除了[]以外,您无需在字符类中转义字符(即使减号也不需要转义,无论是第一个还是最后一个)。

\\\\\\\\是编码正则表达式文字\\的方式(对于Java,一次转义,对于regex一次转义)

这是对此工作的测试:

public static void main(String[] args) { String search = "code:xy"; String newSearch = search.replaceAll("(?=[]\\\\[+&|!(){}^\\"~*?:\\\\\\\\-])", "\\\\\\\\"); System.out.println(newSearch); }

输出:

code\\:xy

这与这个问题密切相关( 如何通过转义&lt;&gt; $ amp;等特殊字符来从URL下载XML文件? )。

这篇文章有一个类似的案例,其中代码下载了具有解析/转义内容的XML。

据我了解,您读取文件,解析文件并转义字符。 在保存期间,XML再次被“转义”。 虽然您可以使用DOM来检查格式正确的XML或架构,但是基于文件的转义操作可以帮助您转义XML和HTML特殊字符。 文章中的代码示例引用了IOUtils和StringUtils的用法。 希望这可以帮助 !

我会在这里使用StringEscapeUtils.escapeXml10()...详细信息。 https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringEscapeUtils.html#ESCAPE_XML10

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM