如何创建/更新可能存在或不存在的XML节点?

时间:2012-07-05 18:59:12

标签: .net xml linq xpath

是否有可用的方法(即没有我创建自己的递归方法),对于给定的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 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:1)

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

答案 2 :(得分: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>

如果“地址”和/或“城市”节点已经存在,这也可以使用。

答案 3 :(得分: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;
    }
}

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