【问题标题】:Best way to compare 2 XML documents in Java在 Java 中比较 2 个 XML 文档的最佳方法
【发布时间】:2010-09-13 14:36:12
【问题描述】:

我正在尝试编写一个应用程序的自动化测试,它基本上将自定义消息格式转换为 XML 消息并将其发送到另一端。我有一组很好的输入/输出消息对,所以我需要做的就是发送输入消息并监听另一端的 XML 消息。

当需要将实际输出与预期输出进行比较时,我遇到了一些问题。我的第一个想法就是对预期和实际消息进行字符串比较。这并不能很好地工作,因为我们拥有的示例数据的格式并不总是一致的,并且经常有不同的别名用于 XML 命名空间(有时根本不使用命名空间。)

我知道我可以解析两个字符串,然后遍历每个元素并自己比较它们,这不会太难做到,但我觉得有更好的方法或我可以利用的库。

所以,归根结底,问题是:

给定两个都包含有效 XML 的 Java 字符串,您将如何确定它们在语义上是否等效?如果您有办法确定差异是什么,则可以加分。

【问题讨论】:

    标签: java xml testing parsing comparison


    【解决方案1】:

    听起来像是 XMLUnit 的工作

    例子:

    public class SomeTest extends XMLTestCase {
      @Test
      public void test() {
        String xml1 = ...
        String xml2 = ...
    
        XMLUnit.setIgnoreWhitespace(true); // ignore whitespace differences
    
        // can also compare xml Documents, InputSources, Readers, Diffs
        assertXMLEqual(xml1, xml2);  // assertXMLEquals comes from XMLTestCase
      }
    }
    

    【讨论】:

    • 我过去在使用 XMLUNit 时遇到过问题,它在 XML API 版本中非常棘手,并且没有被证明是可靠的。不过,自从我为 XOM 放弃它已经有一段时间了,所以也许从那以后它已经改进了。
    • 对于 XMLUnit 的初学者,请注意,默认情况下,如果控制和测试文档的缩进/换行不同,myDiff.similar() 将返回 false。我预计这种行为来自 myDiff.identical(),而不是来自 myDiff.similar()。包括 XMLUnit.setIgnoreWhitespace(true);在您的 setUp 方法中更改测试类中所有测试的行为,或在单个测试方法中使用它来仅更改该测试的行为。
    • @Stew 感谢您的评论,刚从 XMLUnit 开始,我肯定会遇到这个问题。 +1
    • 如果您在 github 上尝试使用 XMLUnit 2,则 2 版本是完全重写的,因此此示例适用于 SourceForge 上的 XMLUnit 1。此外,sourceforge 页面声明“Java 1.x 的 XMLUnit 仍将被维护”。
    • 方法是 assertXMLEqual 来自XMLAssert.java
    【解决方案2】:

    下面将使用标准 JDK 库检查文档是否相等。

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setCoalescing(true); dbf.setIgnoringElementContentWhitespace(true); dbf.setIgnoringComments(true); DocumentBuilder db = dbf.newDocumentBuilder(); 文档 doc1 = db.parse(new File("file1.xml")); doc1.normalizeDocument(); 文档 doc2 = db.parse(new File("file2.xml")); doc2.normalizeDocument(); 断言.assertTrue(doc1.isEqualNode(doc2));

    normalize() 是为了确保没有循环(技术上不会有)

    上面的代码将要求元素中的空格相同,因为它保留并评估它。 Java 附带的标准 XML 解析器不允许您设置功能以提供规范版本或理解 xml:space,如果这会成为问题,那么您可能需要替换 XML 解析器,例如 xerces 或使用 JDOM。

    【讨论】:

    • 这非常适用于没有命名空间或具有“规范化”命名空间前缀的 XML。如果一个 XML 是 而另一个是 ,我怀疑它是否有效
    • dbf.setIgnoringElementContentWhitespace(true) 没有结果我希望 name 不等于 name 使用此解决方案(填充两个空格),但在这种情况下,XMLUnit 给出了相同的结果(JDK8)
    • 对我来说它不会忽略换行符,这是个问题。
    • setIgnoringElementContentWhitespace(false)
    【解决方案3】:

    Xom 有一个 Canonicalizer 实用程序,可将您的 DOM 转换为常规形式,然后您可以对其进行字符串化和比较。因此,无论空格是否不规则或属性排序如何,您都可以对文档进行定期、可预测的比较。

    这在具有专用可视字符串比较器的 IDE 中尤其有效,例如 Eclipse。您可以直观地看到文档之间的语义差异。

    【讨论】:

      【解决方案4】:

      XMLUnit 的最新版本可以帮助断言两个 XML 相等的工作。 XMLUnit.setIgnoreWhitespace()XMLUnit.setIgnoreAttributeOrder() 也可能是相关案例所必需的。

      请参阅下面的 XML 单元使用的简单示例的工作代码。

      import org.custommonkey.xmlunit.DetailedDiff;
      import org.custommonkey.xmlunit.XMLUnit;
      import org.junit.Assert;
      
      public class TestXml {
      
          public static void main(String[] args) throws Exception {
              String result = "<abc             attr=\"value1\"                title=\"something\">            </abc>";
              // will be ok
              assertXMLEquals("<abc attr=\"value1\" title=\"something\"></abc>", result);
          }
      
          public static void assertXMLEquals(String expectedXML, String actualXML) throws Exception {
              XMLUnit.setIgnoreWhitespace(true);
              XMLUnit.setIgnoreAttributeOrder(true);
      
              DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expectedXML, actualXML));
      
              List<?> allDifferences = diff.getAllDifferences();
              Assert.assertEquals("Differences found: "+ diff.toString(), 0, allDifferences.size());
          }
      
      }
      

      如果使用 Maven,请将其添加到您的 pom.xml

      <dependency>
          <groupId>xmlunit</groupId>
          <artifactId>xmlunit</artifactId>
          <version>1.4</version>
      </dependency>
      

      【讨论】:

      • 这非常适合需要从静态方法进行比较的人。
      • 这是完美的答案。谢谢.. 但是我需要忽略不存在的节点。由于我不想在结果输出中看到这样的输出:预期存在子节点“null”但是......我该怎么做?问候。 @acdcjunior
      • XMLUnit.setIgnoreAttributeOrder(true);不起作用。如果某些节点的顺序不同,则比较会失败。
      • [更新] 这个解决方案有效:stackoverflow.com/questions/33695041/…
      • 您确实意识到“IgnoreAttributeOrder”的意思是忽略属性顺序而不是忽略节点顺序,对吧?
      【解决方案5】:

      Tom 的回答为基础,这里是一个使用 XMLUnit v2 的示例。

      它使用这些 maven 依赖项

          <dependency>
              <groupId>org.xmlunit</groupId>
              <artifactId>xmlunit-core</artifactId>
              <version>2.0.0</version>
              <scope>test</scope>
          </dependency>
          <dependency>
              <groupId>org.xmlunit</groupId>
              <artifactId>xmlunit-matchers</artifactId>
              <version>2.0.0</version>
              <scope>test</scope>
          </dependency>
      

      ..这是测试代码

      import static org.junit.Assert.assertThat;
      import static org.xmlunit.matchers.CompareMatcher.isIdenticalTo;
      import org.xmlunit.builder.Input;
      import org.xmlunit.input.WhitespaceStrippedSource;
      
      public class SomeTest extends XMLTestCase {
          @Test
          public void test() {
              String result = "<root></root>";
              String expected = "<root>  </root>";
      
              // ignore whitespace differences
              // https://github.com/xmlunit/user-guide/wiki/Providing-Input-to-XMLUnit#whitespacestrippedsource
              assertThat(result, isIdenticalTo(new WhitespaceStrippedSource(Input.from(expected).build())));
      
              assertThat(result, isIdenticalTo(Input.from(expected).build())); // will fail due to whitespace differences
          }
      }
      

      概述这一点的文档是https://github.com/xmlunit/xmlunit#comparing-two-documents

      【讨论】:

        【解决方案6】:

        谢谢,我扩展了这个,试试这个...

        import java.io.ByteArrayInputStream;
        import java.util.LinkedHashMap;
        import java.util.List;
        import java.util.Map;
        
        import javax.xml.parsers.DocumentBuilder;
        import javax.xml.parsers.DocumentBuilderFactory;
        
        import org.w3c.dom.Document;
        import org.w3c.dom.NamedNodeMap;
        import org.w3c.dom.Node;
        
        public class XmlDiff 
        {
            private boolean nodeTypeDiff = true;
            private boolean nodeValueDiff = true;
        
            public boolean diff( String xml1, String xml2, List<String> diffs ) throws Exception
            {
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setNamespaceAware(true);
                dbf.setCoalescing(true);
                dbf.setIgnoringElementContentWhitespace(true);
                dbf.setIgnoringComments(true);
                DocumentBuilder db = dbf.newDocumentBuilder();
        
        
                Document doc1 = db.parse(new ByteArrayInputStream(xml1.getBytes()));
                Document doc2 = db.parse(new ByteArrayInputStream(xml2.getBytes()));
        
                doc1.normalizeDocument();
                doc2.normalizeDocument();
        
                return diff( doc1, doc2, diffs );
        
            }
        
            /**
             * Diff 2 nodes and put the diffs in the list 
             */
            public boolean diff( Node node1, Node node2, List<String> diffs ) throws Exception
            {
                if( diffNodeExists( node1, node2, diffs ) )
                {
                    return true;
                }
        
                if( nodeTypeDiff )
                {
                    diffNodeType(node1, node2, diffs );
                }
        
                if( nodeValueDiff )
                {
                    diffNodeValue(node1, node2, diffs );
                }
        
        
                System.out.println(node1.getNodeName() + "/" + node2.getNodeName());
        
                diffAttributes( node1, node2, diffs );
                diffNodes( node1, node2, diffs );
        
                return diffs.size() > 0;
            }
        
            /**
             * Diff the nodes
             */
            public boolean diffNodes( Node node1, Node node2, List<String> diffs ) throws Exception
            {
                //Sort by Name
                Map<String,Node> children1 = new LinkedHashMap<String,Node>();      
                for( Node child1 = node1.getFirstChild(); child1 != null; child1 = child1.getNextSibling() )
                {
                    children1.put( child1.getNodeName(), child1 );
                }
        
                //Sort by Name
                Map<String,Node> children2 = new LinkedHashMap<String,Node>();      
                for( Node child2 = node2.getFirstChild(); child2!= null; child2 = child2.getNextSibling() )
                {
                    children2.put( child2.getNodeName(), child2 );
                }
        
                //Diff all the children1
                for( Node child1 : children1.values() )
                {
                    Node child2 = children2.remove( child1.getNodeName() );
                    diff( child1, child2, diffs );
                }
        
                //Diff all the children2 left over
                for( Node child2 : children2.values() )
                {
                    Node child1 = children1.get( child2.getNodeName() );
                    diff( child1, child2, diffs );
                }
        
                return diffs.size() > 0;
            }
        
        
            /**
             * Diff the nodes
             */
            public boolean diffAttributes( Node node1, Node node2, List<String> diffs ) throws Exception
            {        
                //Sort by Name
                NamedNodeMap nodeMap1 = node1.getAttributes();
                Map<String,Node> attributes1 = new LinkedHashMap<String,Node>();        
                for( int index = 0; nodeMap1 != null && index < nodeMap1.getLength(); index++ )
                {
                    attributes1.put( nodeMap1.item(index).getNodeName(), nodeMap1.item(index) );
                }
        
                //Sort by Name
                NamedNodeMap nodeMap2 = node2.getAttributes();
                Map<String,Node> attributes2 = new LinkedHashMap<String,Node>();        
                for( int index = 0; nodeMap2 != null && index < nodeMap2.getLength(); index++ )
                {
                    attributes2.put( nodeMap2.item(index).getNodeName(), nodeMap2.item(index) );
        
                }
        
                //Diff all the attributes1
                for( Node attribute1 : attributes1.values() )
                {
                    Node attribute2 = attributes2.remove( attribute1.getNodeName() );
                    diff( attribute1, attribute2, diffs );
                }
        
                //Diff all the attributes2 left over
                for( Node attribute2 : attributes2.values() )
                {
                    Node attribute1 = attributes1.get( attribute2.getNodeName() );
                    diff( attribute1, attribute2, diffs );
                }
        
                return diffs.size() > 0;
            }
            /**
             * Check that the nodes exist
             */
            public boolean diffNodeExists( Node node1, Node node2, List<String> diffs ) throws Exception
            {
                if( node1 == null && node2 == null )
                {
                    diffs.add( getPath(node2) + ":node " + node1 + "!=" + node2 + "\n" );
                    return true;
                }
        
                if( node1 == null && node2 != null )
                {
                    diffs.add( getPath(node2) + ":node " + node1 + "!=" + node2.getNodeName() );
                    return true;
                }
        
                if( node1 != null && node2 == null )
                {
                    diffs.add( getPath(node1) + ":node " + node1.getNodeName() + "!=" + node2 );
                    return true;
                }
        
                return false;
            }
        
            /**
             * Diff the Node Type
             */
            public boolean diffNodeType( Node node1, Node node2, List<String> diffs ) throws Exception
            {       
                if( node1.getNodeType() != node2.getNodeType() ) 
                {
                    diffs.add( getPath(node1) + ":type " + node1.getNodeType() + "!=" + node2.getNodeType() );
                    return true;
                }
        
                return false;
            }
        
            /**
             * Diff the Node Value
             */
            public boolean diffNodeValue( Node node1, Node node2, List<String> diffs ) throws Exception
            {       
                if( node1.getNodeValue() == null && node2.getNodeValue() == null )
                {
                    return false;
                }
        
                if( node1.getNodeValue() == null && node2.getNodeValue() != null )
                {
                    diffs.add( getPath(node1) + ":type " + node1 + "!=" + node2.getNodeValue() );
                    return true;
                }
        
                if( node1.getNodeValue() != null && node2.getNodeValue() == null )
                {
                    diffs.add( getPath(node1) + ":type " + node1.getNodeValue() + "!=" + node2 );
                    return true;
                }
        
                if( !node1.getNodeValue().equals( node2.getNodeValue() ) )
                {
                    diffs.add( getPath(node1) + ":type " + node1.getNodeValue() + "!=" + node2.getNodeValue() );
                    return true;
                }
        
                return false;
            }
        
        
            /**
             * Get the node path
             */
            public String getPath( Node node )
            {
                StringBuilder path = new StringBuilder();
        
                do
                {           
                    path.insert(0, node.getNodeName() );
                    path.insert( 0, "/" );
                }
                while( ( node = node.getParentNode() ) != null );
        
                return path.toString();
            }
        }
        

        【讨论】:

        • 来晚了,但只是想注意这段代码有一个错误:在 diffNodes() 中,未引用 node2 - 第二个循环错误地重用了 node1(我编辑了代码来解决这个问题) .此外,它有 1 个限制:由于子映射的键控方式,此差异不支持元素名称不唯一的情况,即包含可重复子元素的元素。
        【解决方案7】:

        AssertJ 1.4+ 有特定的断言来比较 XML 内容:

        String expectedXml = "<foo />";
        String actualXml = "<bar />";
        assertThat(actualXml).isXmlEqualTo(expectedXml);
        

        这里是Documentation

        【讨论】:

        • 然而,两个文档之间微不足道的命名空间前缀差异导致 AssertJ 失败。 AssertJ 是一个很棒的工具,但这项工作确实适用于 XMLUnit。
        【解决方案8】:

        下面的代码对我有用

        String xml1 = ...
        String xml2 = ...
        XMLUnit.setIgnoreWhitespace(true);
        XMLUnit.setIgnoreAttributeOrder(true);
        XMLAssert.assertXMLEqual(actualxml, xmlInDb);
        

        【讨论】:

        • 任何上下文?图书馆参考?
        【解决方案9】:

        skaffman 似乎给出了一个很好的答案。

        另一种方法可能是使用命令行实用程序(如 xmlstarlet(http://xmlstar.sourceforge.net/))格式化 XML,然后格式化两个字符串,然后使用任何 diff 实用程序(库)来比较生成的输出文件。当命名空间出现问题时,我不知道这是否是一个好的解决方案。

        【讨论】:

          【解决方案10】:

          我正在使用Altova DiffDog,它具有在结构上比较 XML 文件的选项(忽略字符串数据)。

          这意味着(如果选中“忽略文本”选项):

          <foo a="xxx" b="xxx">xxx</foo>
          

          <foo b="yyy" a="yyy">yyy</foo> 
          

          在结构上相等的意义上是相等的。如果您有数据不同但结构不同的示例文件,这很方便!

          【讨论】:

          • 唯一的缺点是它不是免费的(99 欧元的专业许可证),有 30 天的试用期。
          • 我只找到了实用程序 (altova.com/diffdog/diff-merge-tool.html);很高兴有一个图书馆。
          【解决方案11】:

          我需要与主要问题中要求的功能相同的功能。由于不允许我使用任何 3rd 方库,因此我基于 @Archimedes Trajano 解决方案创建了自己的解决方案。

          以下是我的解决方案。

          import java.io.ByteArrayInputStream;
          import java.nio.charset.Charset;
          import java.util.HashMap;
          import java.util.Map;
          import java.util.Map.Entry;
          import java.util.regex.Matcher;
          import java.util.regex.Pattern;
          import javax.xml.parsers.DocumentBuilder;
          import javax.xml.parsers.DocumentBuilderFactory;
          import javax.xml.parsers.ParserConfigurationException;
          
          import org.junit.Assert;
          import org.w3c.dom.Document;
          
          /**
           * Asserts for asserting XML strings.
           */
          public final class AssertXml {
          
              private AssertXml() {
              }
          
              private static Pattern NAMESPACE_PATTERN = Pattern.compile("xmlns:(ns\\d+)=\"(.*?)\"");
          
              /**
               * Asserts that two XML are of identical content (namespace aliases are ignored).
               * 
               * @param expectedXml expected XML
               * @param actualXml actual XML
               * @throws Exception thrown if XML parsing fails
               */
              public static void assertEqualXmls(String expectedXml, String actualXml) throws Exception {
                  // Find all namespace mappings
                  Map<String, String> fullnamespace2newAlias = new HashMap<String, String>();
                  generateNewAliasesForNamespacesFromXml(expectedXml, fullnamespace2newAlias);
                  generateNewAliasesForNamespacesFromXml(actualXml, fullnamespace2newAlias);
          
                  for (Entry<String, String> entry : fullnamespace2newAlias.entrySet()) {
                      String newAlias = entry.getValue();
                      String namespace = entry.getKey();
                      Pattern nsReplacePattern = Pattern.compile("xmlns:(ns\\d+)=\"" + namespace + "\"");
                      expectedXml = transletaNamespaceAliasesToNewAlias(expectedXml, newAlias, nsReplacePattern);
                      actualXml = transletaNamespaceAliasesToNewAlias(actualXml, newAlias, nsReplacePattern);
                  }
          
                  // nomralize namespaces accoring to given mapping
          
                  DocumentBuilder db = initDocumentParserFactory();
          
                  Document expectedDocuemnt = db.parse(new ByteArrayInputStream(expectedXml.getBytes(Charset.forName("UTF-8"))));
                  expectedDocuemnt.normalizeDocument();
          
                  Document actualDocument = db.parse(new ByteArrayInputStream(actualXml.getBytes(Charset.forName("UTF-8"))));
                  actualDocument.normalizeDocument();
          
                  if (!expectedDocuemnt.isEqualNode(actualDocument)) {
                      Assert.assertEquals(expectedXml, actualXml); //just to better visualize the diffeences i.e. in eclipse
                  }
              }
          
          
              private static DocumentBuilder initDocumentParserFactory() throws ParserConfigurationException {
                  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                  dbf.setNamespaceAware(false);
                  dbf.setCoalescing(true);
                  dbf.setIgnoringElementContentWhitespace(true);
                  dbf.setIgnoringComments(true);
                  DocumentBuilder db = dbf.newDocumentBuilder();
                  return db;
              }
          
              private static String transletaNamespaceAliasesToNewAlias(String xml, String newAlias, Pattern namespacePattern) {
                  Matcher nsMatcherExp = namespacePattern.matcher(xml);
                  if (nsMatcherExp.find()) {
                      xml = xml.replaceAll(nsMatcherExp.group(1) + "[:]", newAlias + ":");
                      xml = xml.replaceAll(nsMatcherExp.group(1) + "=", newAlias + "=");
                  }
                  return xml;
              }
          
              private static void generateNewAliasesForNamespacesFromXml(String xml, Map<String, String> fullnamespace2newAlias) {
                  Matcher nsMatcher = NAMESPACE_PATTERN.matcher(xml);
                  while (nsMatcher.find()) {
                      if (!fullnamespace2newAlias.containsKey(nsMatcher.group(2))) {
                          fullnamespace2newAlias.put(nsMatcher.group(2), "nsTr" + (fullnamespace2newAlias.size() + 1));
                      }
                  }
              }
          
          }
          

          它比较两个 XML 字符串,并通过将它们转换为两个输入字符串中的唯一值来处理任何不匹配的命名空间映射。

          可以进行微调,即在命名空间转换的情况下。但对于我的要求,就可以完成这项工作。

          【讨论】:

            【解决方案12】:

            这将比较完整的字符串 XML(在途中重新格式化它们)。它使您可以轻松使用 IDE(IntelliJ、Eclipse),因为您只需单击并直观地查看 XML 文件中的差异。

            import org.apache.xml.security.c14n.CanonicalizationException;
            import org.apache.xml.security.c14n.Canonicalizer;
            import org.apache.xml.security.c14n.InvalidCanonicalizerException;
            import org.w3c.dom.Element;
            import org.w3c.dom.bootstrap.DOMImplementationRegistry;
            import org.w3c.dom.ls.DOMImplementationLS;
            import org.w3c.dom.ls.LSSerializer;
            import org.xml.sax.InputSource;
            import org.xml.sax.SAXException;
            
            import javax.xml.parsers.DocumentBuilderFactory;
            import javax.xml.parsers.ParserConfigurationException;
            import javax.xml.transform.TransformerException;
            import java.io.IOException;
            import java.io.StringReader;
            
            import static org.apache.xml.security.Init.init;
            import static org.junit.Assert.assertEquals;
            
            public class XmlUtils {
                static {
                    init();
                }
            
                public static String toCanonicalXml(String xml) throws InvalidCanonicalizerException, ParserConfigurationException, SAXException, CanonicalizationException, IOException {
                    Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
                    byte canonXmlBytes[] = canon.canonicalize(xml.getBytes());
                    return new String(canonXmlBytes);
                }
            
                public static String prettyFormat(String input) throws TransformerException, ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
                    InputSource src = new InputSource(new StringReader(input));
                    Element document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
                    Boolean keepDeclaration = input.startsWith("<?xml");
                    DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
                    DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
                    LSSerializer writer = impl.createLSSerializer();
                    writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
                    writer.getDomConfig().setParameter("xml-declaration", keepDeclaration);
                    return writer.writeToString(document);
                }
            
                public static void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
                    String canonicalExpected = prettyFormat(toCanonicalXml(expected));
                    String canonicalActual = prettyFormat(toCanonicalXml(actual));
                    assertEquals(canonicalExpected, canonicalActual);
                }
            }
            

            我更喜欢这个而不是 XmlUnit,因为客户端代码(测试代码)更干净。

            【讨论】:

            • 这在我现在做的两个测试中运行良好,使用相同的 XML 和不同的 XML。使用 IntelliJ diff 它很容易发现比较 XML 中的差异。
            • 顺便说一下,如果你使用 Maven,你将需要这个依赖: org.apache.santuarioxmlsec2.0 .6
            【解决方案13】:

            在 Java 应用程序中使用 JExamXML

                import com.a7soft.examxml.ExamXML;
                import com.a7soft.examxml.Options;
            
                   .................
            
                   // Reads two XML files into two strings
                   String s1 = readFile("orders1.xml");
                   String s2 = readFile("orders.xml");
            
                   // Loads options saved in a property file
                   Options.loadOptions("options");
            
                   // Compares two Strings representing XML entities
                   System.out.println( ExamXML.compareXMLString( s1, s2 ) );
            

            【讨论】:

              【解决方案14】:

              使用 XMLUnit 2.x

              pom.xml

              <dependency>
                  <groupId>org.xmlunit</groupId>
                  <artifactId>xmlunit-assertj3</artifactId>
                  <version>2.9.0</version>
              </dependency>
              

              测试实现(使用junit 5):

              import org.junit.jupiter.api.Test;
              import org.xmlunit.assertj3.XmlAssert;
              
              public class FooTest {
              
                  @Test
                  public void compareXml() {
                      //
                      String xmlContentA = "<foo></foo>";
                      String xmlContentB = "<foo></foo>";
                      //
                      XmlAssert.assertThat(xmlContentA).and(xmlContentB).areSimilar();
                  }
              }
              

              其他方法:areIdentical()areNotIdentical()areNotSimilar()

              更多细节(assertThat(~).and(~) 的配置和示例)在这个documentation page

              XMLUnit 还具有(除其他功能外)DifferenceEvaluator 来进行更精确的比较。

              XMLUnit website

              【讨论】:

                【解决方案15】:

                既然你说“语义等价”,我假设你的意思是你想做的不仅仅是从字面上验证 xml 输出是否(字符串)等于,并且你想要类似

                这里有一些东西

                这里有一些东西

                请以等效方式阅读。最终,您如何在要从中重构消息的任何对象上定义“语义等价”将很重要。只需从消息中构建该对象并使用自定义 equals() 来定义您要查找的内容。

                【讨论】:

                • 不是答案,而是问题。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多