Я использую .NET 2.0, и недавнее изменение кода сделало недействительным мой предыдущий вызов Assert.AreEqual (который сравнивал две строки XML). Только один элемент XML на самом деле отличается в новой кодовой базе, поэтому я надеюсь, что сравнение всех других элементов даст мне желаемый результат. Сравнение нужно проводить программно, так как это часть модульного теста.
Сначала я подумывал об использовании пары экземпляров XmlDocument. Но потом я обнаружил это: http://drowningintechnicaldebt.com/blogs/scottroycraft/archive/2007/05/06/comparing-xml-files.aspx
Похоже, это может сработать, но меня интересовали отзывы о переполнении стека на случай, если есть лучший способ.
Я бы хотел избежать добавления для этого другой зависимости, если это вообще возможно.





Это действительно зависит от того, что вы хотите проверить как «различия».
Прямо сейчас мы используем Microsoft XmlDiff: http://msdn.microsoft.com/en-us/library/aa302294.aspx
Я наткнулся на эту ссылку через сообщение в блоге, на которое я ссылался в вопросе. Я надеялся избежать добавления еще одной зависимости, если это вообще возможно.
Тогда, я думаю, вам нужно решить, в чем «разница» для вас, и разработать свой собственный алгоритм с помощью XmlDocuments, XpathNavigators и т. д. Но я думаю, вы ищете «бизнес-отличия», а не «XML-различия».
В этом случае я знаю, что структура между двумя документами будет одинаковой (без недостающих или лишних элементов). Дело только в том, совпадают ли значения элементов или нет.
Выполнение простого сравнения строк со строкой 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 ) );
}
Спасибо, Джереми, похоже, это хорошее решение. Я попробую.
Поскольку содержимое 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);
}
Я сделал способ создания простых путей 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>
Поскольку этот вопрос был задан впервые, дубликат был поднят с лучшим ответом: stackoverflow.com/a/2924439/361842: Use Linq:
XNode.DeepEquals(doc1, doc2)