2

是否有可用的方法(即无需我创建自己的递归方法),用于给定的 xpath(或其他标识层次位置的方法)来创建/更新 XML 节点,如果该节点不存在,将在其中创建节点?如果父节点也不存在,则需要创建它。我确实有一个包含所有可能节点的 XSD。

即之前:

<employee>
   <name>John Smith</name>
</employee>

想这样称呼:

CoolXmlUpdateMethod("/employee/address/city", "Los Angeles");

后:

  <employee>
       <name>John Smith</name>
       <address>
         <city>Los Angeles</city>
       </address>
    </employee>

或者甚至是一种创建节点的方法,给定一个 xpath,如果父节点不存在,它将递归地创建它们?

就应用程序而言(如果重要的话),这是采用仅包含填充节点的现有 XML 文档,并从另一个系统向其添加数据。新数据可能已经在源 XML 中填充了值,也可能没有。

当然,这不是罕见的情况吗?

4

4 回答 4

2

好吧,我们所做的是创建一个表示 XML 的类(我们使用 XSD2Code 从 XSD 生成一个),当它被反序列化/序列化时,它可以为您做那种事情(XMLSerializer)。

于 2012-07-05T19:01:18.637 回答
2

Chris Knight 的解决方案有一个错误。如果你有:

<a></a>
<b>
  <a>
  </a>
</b> 

and do 
UpdateOrCreate ("<b><a>") 
it will update first node , not nested one.

这是我的功能:

/// <summary>
/// Creates nessecary parent nodes using the provided Queue, and assigns the value to the last child node.
/// </summary>
/// <param name="ele">XElement to take action on</param>
/// <param name="nodes">Queue of node names</param>
/// <param name="value">Value for last child node</param>
/// <param name="attr1Name">Optional name for an attribute, can be null</param>
/// <param name="attr1Val">Optional value for an attribute, can be null</param>
/// returns created/updated element        
public static XElement UpdateOrCreateXmlNode(XElement ele, Queue<string> nodes, string value, string attr1Name = null, string attr1Val = null)
{            
    int fullQueueCOunt = nodes.Count;
    for (int i = 0; i < fullQueueCOunt; i++)
    {
        string node = nodes.Dequeue();
        XElement firstChildMatch = ele.Elements(node).FirstOrDefault();
        if (firstChildMatch == null)
        {
            XElement newChlid = new XElement(node);
            ele.Add(newChlid);
            ele = newChlid;
        }
        else
            ele = firstChildMatch;
    }
    if (attr1Name != null && attr1Val != null)
    {
        if (ele.Attribute(attr1Name) == null)
            ele.Add(new XAttribute(attr1Name, attr1Val));
        else
            ele.Attribute(attr1Name).Value = attr1Val;
    }
    ele.Value = value;
    return ele;
}
于 2014-04-25T08:06:58.857 回答
1

我以前做过这样的事情。我正在使用 LINQ to XML。我为 XElement 创建了一个扩展方法,它采用节点名称队列和列表中最后一个节点的值。这是我做的扩展方法:

/// <summary>
    /// Creates nessecary parent nodes using the provided Queue, and assigns the value to the last child node.
    /// </summary>
    /// <param name="ele">XElement to take action on</param>
    /// <param name="nodes">Queue of node names</param>
    /// <param name="value">Value for last child node</param>
    public static void UpdateOrCreate(this XElement ele, Queue<string> nodes, string value)
    {
        string previousNodeName = "";
        int fullQueueCOunt = nodes.Count;
        for (int i = 0; i < fullQueueCOunt; i++)
        {
            string node = nodes.Dequeue();
            if (ele.Descendants(node).FirstOrDefault() == null)
            {
                if (!string.IsNullOrEmpty(previousNodeName))
                {
                    ele.Element(previousNodeName).Add(new XElement(node));
                }
                else
                {
                    // use main parent node if this is the first iteration
                    ele.Add(new XElement(node));
                }
            }
            previousNodeName = node;
        }
        // assign the value of the last child element
        ele.Descendants(previousNodeName).First().Value = value;
    }

这是一个示例实现:

XElement element = XElement.Parse(
                "<employee>" +
                   "<name>John Smith</name>" +
                "</employee>");
            Queue<string> nodeQueue = new Queue<string>();
            nodeQueue.Enqueue("address");
            nodeQueue.Enqueue("city");
            element.UpdateOrCreate(nodeQueue, "myValue");

这将采用输入 XML:

<employee>
  <name>John Smith</name>
</employee>

并将其更改为:

<employee>
  <name>John Smith</name>
  <address>
    <city>myValue</city>
  </address>
</employee>

如果“地址”和/或“城市”节点已经存在,这也将起作用。

于 2012-07-05T21:13:36.517 回答
0

我自己也在为此苦苦挣扎,所以我想我会添加一个使用 C# 与 .Net 2.0 一起使用的答案。

private static void addOrUpdateNode(XmlDocument xmlDoc, string xpath, string value)
{
    XmlNode node = xmlDoc.SelectSingleNode(xpath);
    if (node == null)
    {
        //node does not exist, so create it
        string newNodeString = String.Format(
            "<city>{0}</city>", value); //as per OP's example
        StringReader sr = new StringReader(newNodeString);
        XmlTextReader reader = new XmlTextReader(sr);
        XmlNode newNode = xmlDoc.ReadNode(reader);
        //adding to root of document, you may want to
        //navigate to a different part of the doc
        xmlDoc.AppendChild(newNode);
    }
    else
    {
        node.Value = value;
    }
}

请原谅我让它变得非常粗糙和未经测试,任何想要清理它的人都可以随意编辑。

于 2012-07-31T09:45:07.867 回答