【问题标题】:How can i escape special characters with using DOM如何使用 DOM 转义特殊字符
【发布时间】:2016-11-23 09:09:36
【问题描述】:

这个问题最近一直困扰着我,我似乎无法找到可能的解决方案。

我正在处理一个接收 XML 文档以进行一些处理的网络服务器。服务器的解析器有 &,',", 的问题。我知道这很糟糕,我没有在该服务器上实现 xml 解析器。但是在等待补丁之前我需要绕过。

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

对于d'ex,我得到d'ex,但我需要d'ex

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

因此,如果 DOM 已经能够将"&" 转义为"&",我该如何让它转义其他字符,例如""

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

这就是我转义我使用 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();


      }

【问题讨论】:

  • 也许&lt;![CDATA[?我认为你应该发布你的转义代码。
  • @RC。我添加了代码:)

标签: java xml dom xml-parsing domdocument


【解决方案1】:

我认为您正在寻找的解决方案是一个自定义的 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> &#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 string replace 是一个很好的起点。

这是关于我对 XSLT 的了解程度,希望它可以帮助您解决问题。

祝你好运。


我正在进一步考虑这一点,解决方案可能不仅仅是 XSLT。根据您的描述,我的印象是,您不是在寻找xml10 编码,而是在寻找一整套html 编码

按照这些思路,如果我们对您当前的节点文本进行转换:

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 文件中的逻辑。您只需确保复制整个节点并复制 text() 并禁用输出转义,而不是管理整个字符串查找/替换。

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

再次警告我对 XSLT 很弱,所以示例 xslt 文件可能 仍然需要一些调整。该解决方案减少了未知的工作 数量,在我看来。

【讨论】:

    【解决方案2】:

    这与这个问题 (how to Download a XML file from a URL by Escaping Special Characters like &lt; &gt; $amp; etc?) 密切相关。

    这篇文章有一个类似的例子,代码下载带有解析/转义内容的 XML。

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

    【讨论】:

      【解决方案3】:

      【讨论】:

      • 如果您查看我的帖子和代码,您会注意到我使用了 StringEscapeUtils。问题出在 StringEscapeUtils 之上,而是在 DOM 或其他 XML 解析器中。
      【解决方案4】:

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

      复制自 (Replace special character with an escape preceded special character in Java)

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

      那个古怪的正则表达式是一个“向前看” - 一个非捕获断言,以下字符匹配某些东西 - 在这种情况下是一个字符类。

      请注意您不需要转义字符类中的字符,除了 ](即使是第一个或最后一个减号也不需要转义)。

      \\\\ 是您编写正则表达式文字 \ 的方式(java 转义一次,正则表达式转义一次)

      这是对这个工作的测试:

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

      输出:

      code\:xy

      【讨论】:

        猜你喜欢
        • 2012-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-05
        • 2014-05-11
        相关资源
        最近更新 更多