Обнаружение структурных различий в XML с использованием Linq и XElement - PullRequest
1 голос
/ 16 февраля 2012

Я пытаюсь проверить какой-то XML, который используется в программном обеспечении на заказ. Я могу обнаружить изменения в идентичных структурах, используя 'XNode.DeepEquals', а затем добавить дополнительный атрибут к измененным элементам, чтобы я мог выделить их.

Моя проблема в том, что, когда структура действительно меняется, эта методология терпит неудачу. (Я перечисляю оба XElements одновременно, выполняя DeepEquals, если они не равны - рекурсивно вызываю один и тот же метод, чтобы отфильтровать, где происходят точные изменения)

Очевидно, что теперь это разваливается, когда я перечисляю, и сравниваемые узлы не совпадают. Смотрите ниже образец:

До

<?xml version="1.0" encoding="utf-16"?>
<Prices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Price default="true">
    <Expression operator="Addition">
        <LeftOperand>
            <AttributeValue field="ccx_bandwidth" />
        </LeftOperand>
        <RightOperand>
            <Constant value="10" type="Integer" />
        </RightOperand>
    </Expression>
</Price>
<Price default="false">
    <Expression operator="Addition">
        <LeftOperand>
            <AttributeValue field="ccx_bandwidth" />
        </LeftOperand>
        <RightOperand>
            <Constant value="99" type="Integer" />
        </RightOperand>
    </Expression>
</Price>
<RollupChildren />

* После 1015 *

<?xml version="1.0" encoding="utf-16"?>
<Prices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Price default="true">
    <Expression operator="Addition">
        <LeftOperand>
            <AttributeValue field="ccx_bandwidth" />
        </LeftOperand>
        <RightOperand>
            <Constant value="10" type="Integer" />
        </RightOperand>
    </Expression>
</Price>
<RollupChildren />

Итак, вы можете видеть, что последний ценовой узел был удален, и мне нужно показать это изменение.

На данный момент у меня есть доступ к обоим фрагментам xml и я изменяю их при загрузке приложения аудита с помощью атрибута auditchanged, который в моем приложении silverlight также связывает фон с помощью конвертера.

Я играл с Linq в Xml и смотрел на объединение двух XElements в запросе, но не знал, как поступить.

В идеале я хотел бы объединить два элемента XElement вместе, но добавить отдельный атрибут в зависимости от того, был ли он добавлен или удален, и который я затем смогу связать с помощью конвертера, чтобы выделить красный или зеленый цвета соответственно.

У кого-нибудь есть какие-нибудь яркие идеи по этому поводу? (Я смотрел на XmlDiff, но не могу использовать это в Silverlight, не думаю?)

Ответы [ 2 ]

0 голосов
/ 16 февраля 2012

У меня есть универсальный класс отличия в библиотеке кодовых блоков http://codeblocks.codeplex.com

Загрузка ваших документов XML и обработка каждого документа как IEnumerable (уплощенное дерево XML) должно позволить вам использовать различия, как показано здесь: http://codeblocks.codeplex.com/wikipage?title=Differ%20Sample&referringTitle=Home

Вот исходный код для dif.cs: http://codeblocks.codeplex.com/SourceControl/changeset/view/96119#1887406

Прототип Diff:

static IEnumerable<DiffEntry> Diff(IEnumerable<T> oldData, IEnumerable<T> newData, Comparison<T> identity, Comparison<T> different)
0 голосов
/ 16 февраля 2012

Важной частью здесь является запрос потомков. Он превращает каждый элемент в первом документе в список его предков, где каждый элемент содержит имя элемента и его индекс среди его одноуровневых элементов. Я думаю, что это может быть как-то использовано для объединения, хотя я понятия не имею, как сделать полное внешнее соединение с linq. Поэтому вместо этого я просто использую эти списки, чтобы найти элементы во втором документе, а затем, в зависимости от результата, возможно, пометить его как удаленный или измененный.

var doc = XDocument.Load(in_A);
var doc2 = XDocument.Load(in_B);
var descendants = doc.Descendants().Select(d => 
    d.AncestorsAndSelf().Reverse().Select(el => 
        new {idx = el.ElementsBeforeSelf(el.Name).Count(), el, name = el.Name}).ToList());

foreach (var list in descendants) {
    XContainer el2 = doc2;
    var el = list.Last().el;
    foreach (var item in list) {
        if (el2 == null) break;
        el2 = el2.Elements(item.name).Skip(item.idx).FirstOrDefault();
    }
    string changed = "";
    if (el2 == null) changed += " deleted";
    else {
        var el2e = el2 as XElement;
        if (el2e.Attributes().Select(a => new { a.Name, a.Value })
            .Except(el.Attributes().Select(a => new { a.Name, a.Value })).Count() > 0) {
                changed += " attributes";
        }
        if (!el2e.HasElements && el2e.Value != el.Value) {
            changed += " value";
        }
        el2e.SetAttributeValue("found", "found");
    }
    if (changed != "") el.SetAttributeValue("changed", changed.Trim());
}
doc.Save(out_A);
doc2.Save(out_B);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...