我認爲解決這個最好的方法是編寫合併使用個類,與我們的區別總是從第一個文檔中訪問節點,並與第二個文檔中的節點並行訪問。
整體設計會是這樣的:
class XmlMerger
{
public XDocument Merge(XDocument first, XDocument second);
private XElement MergeElements(XElement first, XElement second);
private XAttribute MergeAttributes(XAttribute first, XAttribute second);
private XText MergeTexts(XText first, XText second);
}
具體實施看起來是這樣的:
class XmlMerger
{
public XDocument Merge(XDocument first, XDocument second)
{
return new XDocument(MergeElements(first.Root, second.Root));
}
private XElement MergeElements(XElement first, XElement second)
{
if (first == null)
return second;
if (second == null)
return first;
if (first.Name != second.Name)
throw new InvalidOperationException();
var firstId = (string)first.Attribute("id");
var secondId = (string)second.Attribute("id");
// different ids
if (firstId != secondId)
throw new InvalidOperationException();
var result = new XElement(first.Name);
var attributeNames = first.Attributes()
.Concat(second.Attributes())
.Select(a => a.Name)
.Distinct();
foreach (var attributeName in attributeNames)
result.Add(
MergeAttributes(
first.Attribute(attributeName),
second.Attribute(attributeName)));
// text-only elements
if (first.Nodes().OfType<XText>().Any() ||
second.Nodes().OfType<XText>().Any())
{
var firstText = first.Nodes().OfType<XText>().FirstOrDefault();
var secondText = second.Nodes().OfType<XText>().FirstOrDefault();
// we're not handling mixed elements
if (first.Nodes().Any(n => n != firstText) ||
second.Nodes().Any(n => n != secondText))
throw new InvalidOperationException();
result.Add(MergeTexts(firstText, secondText));
}
else
{
var elementNames = first.Elements()
.Concat(second.Elements())
.Select(e => e.Name)
.Distinct();
foreach (var elementName in elementNames)
{
var ids = first.Elements(elementName)
.Concat(second.Elements(elementName))
.Select(e => (string)e.Attribute("id"))
.Distinct();
foreach (var id in ids)
{
XElement firstElement = first.Elements(elementName)
.SingleOrDefault(e => (string)e.Attribute("id") == id);
XElement secondElement = second.Elements(elementName)
.SingleOrDefault(e => (string)e.Attribute("id") == id);
result.Add(MergeElements(firstElement, secondElement));
}
}
}
return result;
}
private XAttribute MergeAttributes(XAttribute first, XAttribute second)
{
if (first == null)
return second;
if (second == null)
return first;
if (first.Name != second.Name)
throw new InvalidOperationException();
if (first.Value == second.Value)
return new XAttribute(first);
// can't merge attributes with different values
throw new InvalidOperationException();
}
private XText MergeTexts(XText first, XText second)
{
if (first == null)
return second;
if (second == null)
return first;
if (first.Value == second.Value)
return new XText(first);
// can't merge texts with different values
throw new InvalidOperationException();
}
}
如果這個代碼遇到一些它不能處理(例如節點具有相同ID但不同的文本;或評論),它會引發異常。
你想要通過ID連接的元素的聯合? – Jodrell 2012-02-13 17:38:10
@Jodrell。 – ARZ 2012-02-13 17:40:02
@ARZ,如果X2中的''的值爲'12345',可能出現這種情況嗎?如果是,如何合併這些文件? –
2012-02-13 17:44:41