Create XML Nodes based on XPath?

后端 未结 12 627
囚心锁ツ
囚心锁ツ 2020-11-27 15:36

Does anyone know of an existing means of creating an XML hierarchy programatically from an XPath expression?

For example if I have an XML fragment such as:

12条回答
  •  旧巷少年郎
    2020-11-27 16:04

    Here's my quick hack that can also create attributes as long as you use a format like /configuration/appSettings/add[@key='name']/@value.

    static XmlNode createXPath(XmlDocument doc, string xpath)
    {
      XmlNode node=doc;
      foreach (string part in xpath.Substring(1).Split('/'))
      {
        XmlNodeList nodes=node.SelectNodes(part);
        if (nodes.Count>1) throw new ComponentException("Xpath '"+xpath+"' was not found multiple times!");
        else if (nodes.Count==1) { node=nodes[0]; continue; }
    
        if (part.StartsWith("@"))
        {
          var anode=doc.CreateAttribute(part.Substring(1));
          node.Attributes.Append(anode);
          node=anode;
        }
        else
        {
          string elName, attrib=null;
          if (part.Contains("["))
          {
            part.SplitOnce("[", out elName, out attrib);
            if (!attrib.EndsWith("]")) throw new ComponentException("Unsupported XPath (missing ]): "+part);
            attrib=attrib.Substring(0, attrib.Length-1);
          }
          else elName=part;
    
          XmlNode next=doc.CreateElement(elName);
          node.AppendChild(next);
          node=next;
    
          if (attrib!=null)
          {
            if (!attrib.StartsWith("@")) throw new ComponentException("Unsupported XPath attrib (missing @): "+part);
            string name, value;
            attrib.Substring(1).SplitOnce("='", out name, out value);
            if (string.IsNullOrEmpty(value) || !value.EndsWith("'")) throw new ComponentException("Unsupported XPath attrib: "+part);
            value=value.Substring(0, value.Length-1);
            var anode=doc.CreateAttribute(name);
            anode.Value=value;
            node.Attributes.Append(anode);
          }
        }
      }
      return node;
    }
    

    SplitOnce is an extension method:

    public static void SplitOnce(this string value, string separator, out string part1, out string part2)
    {
      if (value!=null)
      {
        int idx=value.IndexOf(separator);
        if (idx>=0)
        {
          part1=value.Substring(0, idx);
          part2=value.Substring(idx+separator.Length);
        }
        else
        {
          part1=value;
          part2=null;
        }
      }
      else
      {
        part1="";
        part2=null;
      }
    }
    

    Sample:

    public static void Set(XmlDocument doc, string xpath, string value)
    {
      if (doc==null) throw new ArgumentNullException("doc");
      if (string.IsNullOrEmpty(xpath)) throw new ArgumentNullException("xpath");
    
      XmlNodeList nodes=doc.SelectNodes(xpath);
      if (nodes.Count>1) throw new ComponentException("Xpath '"+xpath+"' was not found multiple times!");
      else if (nodes.Count==0) createXPath(doc, xpath).InnerText=value;
      else nodes[0].InnerText=value;
    }
    

    e.g.

    Set(doc, "/configuration/appSettings/add[@key='Server']/@value", "foobar");
    

提交回复
热议问题