在Java中为XML生成SHA-256哈希值

3
我正在尝试为我的一个项目添加HTTP请求缓存功能,并考虑使用Etag作为哈希值。但是,如果没有Etag,我想使用有效负载来生成唯一的哈希值。我们都知道,相同的XML有效负载可能具有不同的结构。例如,示例A和示例B是相同的。但是它们的字符串结构不同。我需要的是一种从这两个XML示例中生成相同哈希键的方法。

示例A

<note>
   <to>Tove</to>
   <from>Jani</from>
   <heading>Reminder</heading>
   <body>Don't forget me this weekend!</body>
</note>

示例 B

<note>
   <to>Tove</to>
   <heading>Reminder</heading>
   <from>Jani</from>
   <body>Don't forget me this weekend!</body>
</note>

2
如果您的应用程序中顺序不重要,您应该将结构规范化为某个规范顺序,然后进行哈希处理。 - Oliver Charlesworth
1
将XML进行规范化是一项繁重的工作。请参阅数字签名XML的标准。但是为什么您的应用程序会出现这种情况呢?服务器为什么会生成语义上等效但外观完全不同的XML呢? - Thilo
@Thilo 我使用请求有效载荷作为哈希值来缓存响应。请求是由客户端生成的。因此,在某些情况下可能会出现这种情况。 - rnavagamuwa
2
我实际上建议不要费心。我的感觉是这不会成为问题,因为XML将由软件创建,而该软件很可能生成具有相同元素顺序的XML。如果有一些奇怪的情况,那也不太可能对缓存性能产生显著影响。(插入关于过早优化的标准建议。) - Stephen C
1
就 XML 而言,示例 A 和示例 B 并不相同。您的应用程序可能认为它们是等价的,但 XML 不这样认为。因此,无论您进行什么规范化操作都将是特定于应用程序的。 - Wyzard
显示剩余2条评论
1个回答

2

org.w3c.dom.document.normalizeDocument() 方法不会改变子元素的顺序。

您可以通过递归解析文档来实现此操作。但是,请考虑这是否比您首先要缓存的操作更加昂贵...

方法:

  • 在每个级别上将所有节点复制到 java.util.List 实现中,例如 ArrayList。这是必需的,因为 org.w3c.dom.NodeList 不允许修改。
  • 使用 Collections.sort() 对列表进行排序。
  • 从其父项中删除子项。
  • 按排序后的顺序添加子项。

请注意,这不处理具有相同名称但内容不同的多个元素,但可以解决您的示例问题。

例如:

public static void main(String[] args) throws Exception {
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("test.xml"));
    sort(doc);

    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer transformer = tf.newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    StringWriter writer = new StringWriter();
    transformer.transform(new DOMSource(doc), new StreamResult(writer));

    System.out.println(writer);
}

private static void sort(Node doc) {
    List<Node> children = new ArrayList<>();
    for (int i = 0; i < doc.getChildNodes().getLength(); i++) {
        children.add(doc.getChildNodes().item(i));
    }
    for (Node child : children) {
        doc.removeChild(child);
    }
    Collections.sort(children, (a, b) -> {
        return a.getNodeName().compareTo(b.getNodeName());
    });
    for (Node child : children) {
        doc.appendChild(child);
    }
    for (Node child : children) {
        sort(child);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接