Как лучше всего сравнивать файлы XML на предмет равенства?

Я использую .NET 2.0, и недавнее изменение кода сделало недействительным мой предыдущий вызов Assert.AreEqual (который сравнивал две строки XML). Только один элемент XML на самом деле отличается в новой кодовой базе, поэтому я надеюсь, что сравнение всех других элементов даст мне желаемый результат. Сравнение нужно проводить программно, так как это часть модульного теста.

Сначала я подумывал об использовании пары экземпляров XmlDocument. Но потом я обнаружил это: http://drowningintechnicaldebt.com/blogs/scottroycraft/archive/2007/05/06/comparing-xml-files.aspx

Похоже, это может сработать, но меня интересовали отзывы о переполнении стека на случай, если есть лучший способ.

Я бы хотел избежать добавления для этого другой зависимости, если это вообще возможно.

Похожие вопросы

Поскольку этот вопрос был задан впервые, дубликат был поднят с лучшим ответом: stackoverflow.com/a/2924439/361842: Use Linq: XNode.DeepEquals(doc1, doc2)

JohnLBevan 10.10.2018 21:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
1
14 561
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Это действительно зависит от того, что вы хотите проверить как «различия».

Прямо сейчас мы используем Microsoft XmlDiff: http://msdn.microsoft.com/en-us/library/aa302294.aspx

Я наткнулся на эту ссылку через сообщение в блоге, на которое я ссылался в вопросе. Я надеялся избежать добавления еще одной зависимости, если это вообще возможно.

Scott Lawrence 18.11.2008 20:59

Тогда, я думаю, вам нужно решить, в чем «разница» для вас, и разработать свой собственный алгоритм с помощью XmlDocuments, XpathNavigators и т. д. Но я думаю, вы ищете «бизнес-отличия», а не «XML-различия».

Filini 19.11.2008 17:05

В этом случае я знаю, что структура между двумя документами будет одинаковой (без недостающих или лишних элементов). Дело только в том, совпадают ли значения элементов или нет.

Scott Lawrence 20.11.2008 17:26

Выполнение простого сравнения строк со строкой xml не всегда работает. Почему ?

например оба:

<MyElement></MyElmennt> и <MyElment/> равны с точки зрения xml.

Существуют алгоритмы конвертации, чтобы xml всегда выглядел одинаково, они называются алгоритмы канонизации. .Net поддерживает канонизацию.

Возможно, вы сочтете менее хрупким анализировать XML в XmlDocument и основывать свои вызовы Assert на запросе XPath. Вот несколько вспомогательных методов утверждения, которые я часто использую. Каждый из них принимает XPathNavigator, который можно получить, вызвав CreateNavigator () в XmlDocument или на любом узле, извлеченном из документа. Пример использования:

     XmlDocument doc = new XmlDocument( "Testdoc.xml" );
     XPathNavigator nav = doc.CreateNavigator();
     AssertNodeValue( nav, "/root/foo", "foo_val" );
     AssertNodeCount( nav, "/root/bar", 6 )

    private static void AssertNodeValue(XPathNavigator nav,
                                         string xpath, string expected_val)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNotNull(node, "Node '{0}' not found", xpath);
        Assert.AreEqual( expected_val, node.Value );
    }

    private static void AssertNodeExists(XPathNavigator nav,
                                         string xpath)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNotNull(node, "Node '{0}' not found", xpath);
    }

    private static void AssertNodeDoesNotExist(XPathNavigator nav,
                                         string xpath)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNull(node, "Node '{0}' found when it should not exist", xpath);
    }

    private static void AssertNodeCount(XPathNavigator nav, string xpath, int count)
    {
        XPathNodeIterator nodes = nav.Select( xpath, nav );
        Assert.That( nodes.Count, Is.EqualTo( count ) );
    }

Спасибо, Джереми, похоже, это хорошее решение. Я попробую.

Scott Lawrence 18.11.2008 23:11

Поскольку содержимое XML-файла может иметь разное форматирование и по-прежнему считаться одним и тем же (с точки зрения DOM), когда вы проверяете равенство, вам необходимо определить, какова мера этого равенства, например, игнорируется ли форматирование? игнорируются ли метаданные и т. д. важно позиционирование, много крайних случаев.

Как правило, вы должны создать класс, который определяет ваши правила равенства и использовать его для ваших сравнений, и если ваш класс сравнения реализует интерфейсы IEqualityComparer and/or IEqualityComparer<T>, то ваш класс можно использовать во множестве встроенных списков фреймворков в качестве реализации теста равенства. Плюс, конечно, у вас может быть столько, сколько вам нужно, чтобы измерить равенство по-разному, как того требуют ваши требования.

т.е.

IEnumerable<T>.Contains
IEnumerable<T>.Equals
The constructior of a Dictionary etc etc

В итоге я получил желаемый результат с помощью следующего кода:

private static void ValidateResult(string validationXml, XPathNodeIterator iterator, params string[] excludedElements)
    {
        while (iterator.MoveNext())
        {
            if (!((IList<string>)excludedElements).Contains(iterator.Current.Name))
            {
                Assert.IsTrue(validationXml.Contains(iterator.Current.Value), "{0} is not the right value for {1}.", iterator.Current.Value, iterator.Current.Name);
            }
        }
    }

Перед вызовом метода я создаю навигатор на экземпляре XmlDocument следующим образом:

XPathNavigator nav = xdoc.CreateNavigator();

Затем я создаю экземпляр XPathExpression, например:

XPathExpression expression = XPathExpression.Compile("/blah/*");

Я вызываю метод после создания итератора с выражением:

XPathNodeIterator iterator = nav.Select(expression);

Я все еще пытаюсь понять, как его оптимизировать, но пока это помогает.

Я написал небольшую библиотеку с утверждениями для сериализации, источник.

Образец:

[Test]
public void Foo()
{
   ...
   XmlAssert.Equal(expected, actual, XmlAssertOptions.IgnoreDeclaration | XmlAssertOptions.IgnoreNamespaces);
}

NuGet

Я сделал способ создания простых путей XML.

static XElement MakeFromXPath(string xpath)
{
    XElement root = null;
    XElement parent = null;
    var splits = xpath.Split('/'); //split xpath into parts
    foreach (var split in splits)
    {
        var el = new XElement(split);
        if (parent != null)
            parent.Add(el);
        else
            root = el; //first element created, set as root
        parent = el;
    }
    return root;
}

Пример использования:

var element = MakeFromXPath("My/Path/To/Element")'

element будет содержать значение:

<My>
  <Path>
    <To>
      <Element></Element>
    </To>
  </Path>
</My>

Другие вопросы по теме