【问题标题】:Check if XML element equals another XML element, ignoring empty values检查 XML 元素是否等于另一个 XML 元素,忽略空值
【发布时间】:2020-02-23 16:12:15
【问题描述】:

我正在检查字符串给出的两个 XML 元素是否与此方法相等:

private static bool XmlEquals(string s1, string s2)
{
    return XNode.DeepEquals(XElement.Parse(s1), XElement.Parse(s2));
}

除非其中一个元素有开始和结束标签,而另一个元素有像这样的结束标签,否则此方法有效:

<MyElement SomeAttribute="some value" />
<MyElement SomeAttribute="some value"></MyElement>

我能否以某种方式比较两个 XML 元素,使上述情况被视为相等

【问题讨论】:

标签: c# xml


【解决方案1】:

解决这个特殊问题的直接方法是introduce closing brackets explicitly

 private static bool XmlEquals(string s1, string s2)
 {
      var firstElement = XElement.Parse(s1);
      var secondElement = XElement.Parse(s2);
      IntroduceClosingBracket(firstElement);
      IntroduceClosingBracket(secondElement);

      return XNode.DeepEquals(firstElement, secondElement);
 }

 private static void IntroduceClosingBracket(XElement element)
 {
      foreach (var descendant in element.DescendantsAndSelf())
      {
           if (descendant.IsEmpty)
           {
                descendant.SetValue(String.Empty);
           }
      }
 }

不过,遍历所有后代可能会导致性能下降。

【讨论】:

  • 这是迄今为止我见过的最好的解决方案,它可以工作,但我必须在IntroduceClosingBracket 方法中添加if (element.IsEmpty) { element.SetValue(string.Empty); } 才能使其正常工作。
  • @Hinek 哦,我真傻,我从一开始就为嵌套元素编写了一个测试,并没有检查明显的情况。我会解决的,谢谢!
  • 现在很漂亮。谢谢,你帮了大忙!
  • @Hinek 不客气!您可能想查看 J.Steen 评论中的 link。 XML 比较还有其他问题。
【解决方案2】:

如果 XML 不必像 @defaultlocale 的解决方案那样完全相同,它真正比较所有内容(甚至是 cmets),您可以使用 LINQ 仅比较您感兴趣的内容。展示我提出的想法属性值的比较可选。

测试数据:

var xml1 = @"
    <elm1 attr1='val1'>
        <elm2 attr2='val1'>
            <elm3 attr3='val1' />
        </elm2>
    </elm1>";

var xml2 = @"
    <elm1 attr1='val1'>
        <elm2 attr2='val1'>
            <elm3 attr3='val1' attr='val2' />
        </elm2>
    </elm1>";

递归比较:

// Just a helper.
private static Tuple<XElement, XElement> Compare(
    string xml1, 
    string xml2, 
    bool compareAttributeValues)
{
    return Compare(
        XElement.Parse(xml1), 
        XElement.Parse(xml2), 
        compareAttributeValues);
}

// Compares XElements recursively
// and returns the two nodes that are different if any.
private static Tuple<XElement, XElement> Compare(
    XElement xElm1,
    XElement xElm2, 
    bool compareAttributeValues)
{
    // Elements are different if they have a different number of children.
    if (xElm1.Elements().Count() != xElm2.Elements().Count())
    {
        return new Tuple<XElement, XElement>(xElm1, xElm2);
    }

    // Enumerate both elements at the same time.
    var elements = Enumerable.Zip(
        xElm1.Elements(), 
        xElm2.Elements(), 
        (x, y) => new Tuple<XElement, XElement>(x, y));
    foreach (var item elements )
    {
        // Elements are equal if they have the same names...
        bool haveSameNames = xElm1.Name.LocalName == xElm2.Name.LocalName;

        // and the same attributes.
        bool haveSameAttributes =
            item.Item1
            // Concatenate and group attributes by their name.
            .Attributes()
            .Concat(item.Item2.Attributes())
            .GroupBy(x => x.Name.LocalName, (key, items) => new
            {
                Name = key,
                Items = items,
                // Attiribute value comparison can be skipped.
                // If enabled compare attribute values.
                // They are equal if the result is only one group.
                HaveSameValues = 
                    compareAttributeValues == false 
                    || items.GroupBy(y => y.Value).Count() == 1,
                Count = items.Count()
            })
            // Each attribute group must contain two items
            // if they are identical (one per element).
            .Where(x => x.Count != 2 || x.HaveSameValues == false)
            .Any() == false;
        if (!haveSameNames || !haveSameAttributes)
        {
            return item;
        }
        else
        {
            // Return the different nodes.
            return Compare(item.Item1, item.Item2, compareAttributeValues);
        }
    }
    // No differences found.
    return null;
}

【讨论】:

  • 如果有时我的元素有子元素怎么办?
  • 您可以在递归方法中调用它,并将每个元素相互比较,如果元素相等则更深入,直到找到差异。
  • 您的示例非常简单,仅比较两个元素。你没有说任何关于树或递归的事情。
  • 是的,我的问题中没有,但它可能会发生,所以我真的很想依靠框架的逻辑进行比较。我将使用“默认语言环境”的解决方案。还是谢谢。
  • 我已经更新了代码以供将来参考,并且我已经让它递归了。
【解决方案3】:

当有空标签时使用 XNode.DeepEquals 的问题, 比较 XML 文档中所有 XML 元素的最佳方法(即使 XML 结束标记不同,这也应该有效)

    public bool CompareXml()
    {
            var doc = @"
            <ContactPersons>
                <ContactPersonRole>General</ContactPersonRole>
                <Person>
                  <Name>Aravind Kumar Eriventy</Name>
                  <Email/>
                  <Mobile>9052534488</Mobile>
                </Person>
              </ContactPersons>";

            var doc1 = @"
            <ContactPersons>
                <ContactPersonRole>General</ContactPersonRole>
                <Person>
                  <Name>Aravind Kumar Eriventy</Name>
                  <Email></Email>
                  <Mobile>9052534488</Mobile>
                </Person>
              </ContactPersons>";

        return XmlDocCompare(XDocument.Parse(doc), XDocument.Parse(doc1));

    }

    private static bool XmlDocCompare(XDocument doc,XDocument doc1)
    {
        IntroduceClosingBracket(doc.Root);
        IntroduceClosingBracket(doc1.Root);

        return XNode.DeepEquals(doc1, doc);
    }

    private static void IntroduceClosingBracket(XElement element)
    {
        foreach (var descendant in element.DescendantsAndSelf())
        {
            if (descendant.IsEmpty)
            {
                descendant.SetValue(String.Empty);
            }
        }
    }

【讨论】:

    【解决方案4】:

    你总是可以在引号处分割字符串,然后比较它们。

    s1Array = s1.Split('\"');
    s2Array = s2.Split('\"');
    
    if(s1[0] == s2[0] && s1[1] == s2[1])
        return true;
    

    忽略空值就像检查一样简单

    if(s1[1] == String.Empty || s1[1] == null)...
    

    【讨论】:

    • 这究竟有什么帮助?这仅适用于极其有限的情况。
    猜你喜欢
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 2011-05-16
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多