gpt4 book ai didi

c# - 合并两个 XML 文件并添加缺少的标签和属性

转载 作者:数据小太阳 更新时间:2023-10-29 01:48:00 24 4
gpt4 key购买 nike

我有两个基本格式相同的 XML 文件,但是 Master.XML 中的一些标签和属性不包含在 Child.XML 中。

我需要将 XML 文件合并到一个新的 XML 文件中,其中缺少标签和属性。

如果 Master.XML 和 Child.XML 中的值不同,则应使用 Child.XML 中的值。

我尝试对节点使用 Union 和 Concat,但它不起作用。

  master.DescendantNodes().Union(child.DescendantNodes());  

任何建议都会有所帮助。

Master.XML

<SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
<DbPath>C:\Agilent_i1000\ICPT_DB.sqlite</DbPath>
<CardDiagonsticsDelayTime>10</CardDiagonsticsDelayTime>
<ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
<ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
<ScreenSpec Name="1" Xrelative="75" Yrelative="0" NotToUse="1"></ScreenSpec>
<ScreenSpec Name="2" Xrelative="75" Yrelative="25" NotToUse="1"></ScreenSpec>
</ScreenSpecs>
</SysConfig>

Child.XML

<SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
<CardDiagonsticsDelayTime>20</CardDiagonsticsDelayTime>
<ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
<ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
<ScreenSpec Name="1" Xrelative="100" Yrelative="0" ></ScreenSpec>
<ScreenSpec Name="2" Xrelative="75" Yrelative="25"></ScreenSpec>
<ScreenSpec Name="3" Xrelative="175" Yrelative="25"></ScreenSpec>
</ScreenSpecs>
</SysConfig>

预期输出

    <SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
<DbPath>C:\Agilent_i1000\ICPT_DB.sqlite</DbPath>
<CardDiagonsticsDelayTime>20</CardDiagonsticsDelayTime>
<ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
<ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
<ScreenSpec Name="1" Xrelative="100" Yrelative="0" NotToUse="1" ></ScreenSpec>
<ScreenSpec Name="2" Xrelative="75" Yrelative="25" NotToUse="1"></ScreenSpec>
<ScreenSpec Name="3" Xrelative="175" Yrelative="25">
</ScreenSpec>
</ScreenSpecs>
</SysConfig>

最佳答案

这是一组能够合并两个 XML 文档的实用程序类。整个东西非常通用,但您可能需要对其进行修改以满足其他特定需求。

这是处理您的案例的代码:

    var result = new XmlXPathDocument();
result.Load("master.xml");

var child = new XmlXPathDocument();
child.Load("child.xml");

// here we tell the class that "Name" is a discriminant attribute.
// two nodes at the same level with the same discriminant attributes are considered the same, so will be merged
// if we don't do this, they will considered different and they will be added if the set of their attributes is different
child.AddDiscriminantAttribute("Name", string.Empty);
child.InjectXml(result);

result.Save("output.xml");

这些类派生自标准 XmlDocument 类层次结构。他们使用自动计算的 XPATH 表达式来逐个节点地合并文档。

public class XmlXPathDocument : XmlDocument
{
public const string XmlNamespaceUri = "http://www.w3.org/2000/xmlns/";
public const string XmlNamespacePrefix = "xmlns";

internal List<Tuple<string, string>> _discriminantAttributes = new List<Tuple<string, string>>();

public XmlXPathDocument() => Construct();
public XmlXPathDocument(XmlNameTable nameTable) : base(nameTable) => Construct();
public XmlXPathDocument(XmlImplementation implementation) : base(implementation) => Construct();

protected virtual void Construct() => XPathNamespaceManager = new XmlNamespaceManager(new NameTable());

public virtual XmlNamespaceManager XPathNamespaceManager { get; private set; }

public override XmlElement CreateElement(string prefix, string localName, string namespaceURI) => new XmlXPathElement(prefix, localName, namespaceURI, this);

public override XmlCDataSection CreateCDataSection(string data) => new XmlXPathCDataSection(data, this);

public override XmlText CreateTextNode(string text) => new XmlXPathText(text, this);

public virtual void AddDiscriminantAttribute(string name, string namespaceURI)
{
if (name == null)
throw new ArgumentNullException(nameof(name));

_discriminantAttributes.Add(new Tuple<string, string>(name, namespaceURI));
}

public virtual bool IsDiscriminant(XmlAttribute attribute)
{
if (attribute == null)
throw new ArgumentNullException(nameof(attribute));

foreach (var pair in _discriminantAttributes)
{
string ns = Nullify(attribute.NamespaceURI);
string dns = Nullify(pair.Item2);
if (ns == dns && pair.Item1 == attribute.LocalName)
return true;
}
return false;
}

private static string Nullify(string text)
{
if (text == null)
return null;

text = text.Trim();
if (text.Length == 0)
return null;

return text;
}

internal string GetPrefix(string namespaceURI)
{
if (string.IsNullOrEmpty(namespaceURI))
return null;

string prefix = XPathNamespaceManager.LookupPrefix(namespaceURI);
if (!string.IsNullOrEmpty(prefix))
{
XPathNamespaceManager.AddNamespace(prefix, namespaceURI);
return prefix;
}

string newPrefix;
int index = 0;
do
{
newPrefix = "ns" + index;
if (XPathNamespaceManager.LookupNamespace(newPrefix) == null)
break;

index++;
}
while (true);
XPathNamespaceManager.AddNamespace(newPrefix, namespaceURI);
return newPrefix;
}

private static bool IsNamespaceAttribute(XmlAttribute attribute)
{
if (attribute == null)
return false;

return attribute.NamespaceURI == XmlNamespaceUri && attribute.Prefix == XmlNamespacePrefix;
}

private static IEnumerable<XmlAttribute> GetAttributes(IXmlXPathNode node)
{
var xe = node as XmlElement;
if (xe == null)
yield break;

foreach (XmlAttribute att in xe.Attributes)
{
yield return att;
}
}

private static XmlAttribute GetAttribute(IXmlXPathNode node, string name) => node is XmlElement xe ? xe.Attributes[name] : null;
private static XmlAttribute GetAttribute(IXmlXPathNode node, string localName, string ns) => node is XmlElement xe ? xe.Attributes[localName, ns] : null;

public virtual bool InjectXml(XmlDocument target)
{
if (target == null)
throw new ArgumentNullException(nameof(target));

if (DocumentElement == null)
return false;

bool changed = false;
foreach (XmlNode node in SelectNodes("//node()"))
{
var xelement = node as IXmlXPathNode;
if (xelement == null)
continue;

if (string.IsNullOrEmpty(xelement.XPathExpression))
continue;

XmlNode other = target.SelectSingleNode(xelement.XPathExpression, XPathNamespaceManager);
if (other != null)
{
if (other is XmlElement otherElement)
{
foreach (XmlAttribute att in GetAttributes(xelement))
{
if (IsNamespaceAttribute(att))
continue;

if (otherElement.Attributes[att.LocalName, att.NamespaceURI]?.Value != att.Value)
{
otherElement.SetAttribute(att.LocalName, att.NamespaceURI, att.Value);
changed = true;
}
}
continue;
}
}

if (node is XmlXPathElement element)
{
XmlElement parent = EnsureTargetParent(xelement, target, out changed);
XmlElement targetElement = target.CreateElement(element.LocalName, element.NamespaceURI);
changed = true;
if (parent == null)
{
target.AppendChild(targetElement);
}
else
{
parent.AppendChild(targetElement);
}

foreach (XmlAttribute att in GetAttributes(xelement))
{
if (IsNamespaceAttribute(att))
continue;

targetElement.SetAttribute(att.LocalName, att.NamespaceURI, att.Value);
}
continue;
}

if (node is XmlXPathCDataSection cdata)
{
XmlElement parent = EnsureTargetParent(xelement, target, out changed);
var targetCData = target.CreateCDataSection(cdata.Value);
changed = true;
if (parent == null)
{
target.AppendChild(targetCData);
AppendNextTexts(node, targetCData, target);
}
else
{
if (parent.ChildNodes.Count == 1 && parent.ChildNodes[0] is XmlCharacterData)
{
parent.RemoveChild(parent.ChildNodes[0]);
}
parent.AppendChild(targetCData);
AppendNextTexts(node, targetCData, parent);
}
continue;
}

if (node is XmlXPathText text)
{
XmlElement parent = EnsureTargetParent(xelement, target, out changed);
var targetText = target.CreateTextNode(text.Value);
changed = true;
if (parent == null)
{
target.AppendChild(targetText);
AppendNextTexts(node, targetText, target);
}
else
{
if (parent.ChildNodes.Count == 1 && parent.ChildNodes[0] is XmlCharacterData)
{
parent.RemoveChild(parent.ChildNodes[0]);
}
parent.AppendChild(targetText);
AppendNextTexts(node, targetText, parent);
}
continue;
}
}
return changed;
}

private static void AppendNextTexts(XmlNode textNode, XmlNode targetTextNode, XmlNode parent)
{
do
{
if (textNode.NextSibling is XmlText text)
{
var newText = targetTextNode.OwnerDocument.CreateTextNode(text.Value);
parent.AppendChild(newText);
}
else
{
var cdata = textNode.NextSibling as XmlCDataSection;
if (cdata == null)
break;

var newCData = targetTextNode.OwnerDocument.CreateCDataSection(cdata.Value);
parent.AppendChild(newCData);
}
textNode = textNode.NextSibling;
}
while (true);
}

private static XmlElement EnsureTargetParent(IXmlXPathNode element, XmlDocument target, out bool changed)
{
changed = false;
if (element.ParentNode is XmlXPathElement parent)
{
if (string.IsNullOrEmpty(parent.XPathExpression))
return null;

if (target.SelectSingleNode(parent.XPathExpression, element.OwnerDocument.XPathNamespaceManager) is XmlElement targetElement)
return targetElement;

var parentElement = EnsureTargetParent(parent, target, out changed);
targetElement = target.CreateElement(parent.LocalName, parent.NamespaceURI);
parentElement.AppendChild(targetElement);
changed = true;
return targetElement;
}
return target.DocumentElement;
}
}

public class XmlXPathElement : XmlElement, IXmlXPathNode
{
private Lazy<string> _xPathExpression;

public XmlXPathElement(string prefix, string localName, string namespaceURI, XmlXPathDocument doc) : base(prefix, localName, namespaceURI, doc)
{
_xPathExpression = new Lazy<string>(() => GetXPathExpression());
}

public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
public virtual string XPathExpression => _xPathExpression.Value;

private static string GetAttEscapedValue(string value)
{
if (value.IndexOf('\'') >= 0)
return "=\"" + value.Replace("\"", "&quot;") + "\"";

return "='" + value + "'";
}

private string GetDiscriminantAttributeXPath()
{
foreach (var att in OwnerDocument._discriminantAttributes)
{
XmlAttribute disc;
if (string.IsNullOrEmpty(att.Item2))
{
disc = GetAttributeNode(att.Item1);
}
else
{
disc = GetAttributeNode(att.Item1, att.Item2);
}

if (disc != null)
{
string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
string name = Name + "[@" + disc.Name + GetAttEscapedValue(disc.Value) + "]";
if (newPrefix != null)
{
name = newPrefix + ":" + name;
}
return name;
}
}
return null;
}

private string GetAttributesXPath()
{
if (Attributes.Count == 0)
return null;

var sb = new StringBuilder();
foreach (XmlAttribute att in Attributes)
{
if (sb.Length > 0)
{
sb.Append(" and ");
}

sb.Append("@");
sb.Append(att.Name);
sb.Append(GetAttEscapedValue(att.Value));
OwnerDocument.GetPrefix(att.NamespaceURI);
}

var text = sb.ToString().Trim();
if (text.Length == 0)
return null;

return "[" + text + "]";
}

private string GetXPath(XmlNodeList parentNodes)
{
string discriminant = GetDiscriminantAttributeXPath();
if (discriminant != null)
return discriminant;

string name = Name;
string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
if (newPrefix != null)
{
name = newPrefix + ":" + LocalName;
}

if (parentNodes.Count == 1)
return name;

var sameName = new List<XmlElement>();
foreach (XmlNode node in parentNodes)
{
if (node.NodeType != XmlNodeType.Element)
continue;

if (node.Name == Name)
{
sameName.Add((XmlElement)node);
}
}

if (sameName.Count == 1)
return name;

string byIndex = null;
var sameAtts = new List<XmlElement>();
for (int i = 0; i < sameName.Count; i++)
{
if (sameName[i] == this)
{
byIndex = name + "[" + (i + 1) + "]";
continue;
}

bool same = true;
foreach (XmlAttribute att in Attributes)
{
XmlAttribute sameAtt = sameName[i].Attributes[att.LocalName, att.NamespaceURI];
if (sameAtt == null || string.Compare(sameAtt.Value, att.Value, StringComparison.OrdinalIgnoreCase) != 0)
{
same = false;
break;
}
}

if (same)
{
sameAtts.Add(sameName[i]);
}
}

if (sameAtts.Count == 0)
return name + GetAttributesXPath();

return byIndex;
}

private string GetXPathExpression()
{
if (ParentNode == null)
{
string name = Name;
string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
if (newPrefix != null)
{
name = newPrefix + ":" + name;
}
return name;
}

string expr = GetXPath(ParentNode.ChildNodes);
if (ParentNode is XmlXPathElement parent)
{
expr = parent.XPathExpression + "/" + expr;
}

if (ParentNode.NodeType == XmlNodeType.Document)
{
expr = "/" + expr;
}
return expr;
}
}

public class XmlXPathText : XmlText, IXmlXPathNode
{
private Lazy<string> _xPathExpression;

public XmlXPathText(string data, XmlXPathDocument doc) : base(data, doc)
{
_xPathExpression = new Lazy<string>(() => GetTextXPathExpression(this));
}

public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
public virtual string XPathExpression => _xPathExpression.Value;

internal static string GetTextXPathExpression(XmlNode node)
{
if (node.ParentNode is IXmlXPathNode element)
return element.XPathExpression + "/text()";

return null;
}
}

public class XmlXPathCDataSection : XmlCDataSection, IXmlXPathNode
{
private Lazy<string> _xPathExpression;

public XmlXPathCDataSection(string data, XmlXPathDocument doc) : base(data, doc)
{
_xPathExpression = new Lazy<string>(() => XmlXPathText.GetTextXPathExpression(this));
}

public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
public virtual string XPathExpression => _xPathExpression.Value;
}

public interface IXmlXPathNode
{
string XPathExpression { get; }
XmlNode ParentNode { get; }
XmlXPathDocument OwnerDocument { get; }
}

关于c# - 合并两个 XML 文件并添加缺少的标签和属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49483631/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com