当前位置:  开发笔记 > 编程语言 > 正文

基于XPath创建XML节点?

如何解决《基于XPath创建XML节点?》经验,为你挑选了4个好方法。

有没有人知道从XPath表达式以编程方式创建XML层次结构的现有方法?

例如,如果我有一个XML片段,例如:


    
        
        
    

给定XPath表达式/ feed/entry/content/@ source我会:


    
        
        
    

我意识到使用XSLT是可能的,但由于我正在尝试完成的动态特性,固定转换将无法工作.

我在C#工作,但如果有人使用其他语言的解决方案,请加入.

谢谢您的帮助!



1> xcud..:

在您提供的示例中,唯一创建的是属性...

XmlElement element = (XmlElement)doc.SelectSingleNode("/feed/entry/content");
if (element != null)
    element.SetAttribute("source", "");

如果你真正想要的是能够建立在它不那么你可以自己简单的XPath语法分析器中存在的层次结构.我不知道将属性保留在xpath中.我宁愿把节点作为一个元素来构建并在.SetAttribute上添加,就像我在这里所做的那样:

static private XmlNode makeXPath(XmlDocument doc, string xpath)
{
    return makeXPath(doc, doc as XmlNode, xpath);
}

static private XmlNode makeXPath(XmlDocument doc, XmlNode parent, string xpath)
{
    // grab the next node name in the xpath; or return parent if empty
    string[] partsOfXPath = xpath.Trim('/').Split('/');
    string nextNodeInXPath = partsOfXPath.First();
    if (string.IsNullOrEmpty(nextNodeInXPath))
        return parent;

    // get or create the node from the name
    XmlNode node = parent.SelectSingleNode(nextNodeInXPath);
    if (node == null)
        node = parent.AppendChild(doc.CreateElement(nextNodeInXPath));

    // rejoin the remainder of the array as an xpath expression and recurse
    string rest = String.Join("/", partsOfXPath.Skip(1).ToArray());
    return makeXPath(doc, node, rest);
}

static void Main(string[] args)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("");

    makeXPath(doc, "/feed/entry/data");
    XmlElement contentElement = (XmlElement)makeXPath(doc, "/feed/entry/content");
    contentElement.SetAttribute("source", "");

    Console.WriteLine(doc.OuterXml);
}


有用的功能,但是如果您可以传递正确的XPath语法(例如,nodea / nodeb [1] / nodec :-),它将更加有用。

2> laktak..:

这是我的快速黑客,只要你使用像这样的格式,也可以创建属性/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是一种扩展方法:

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;
  }
}

样品:

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;
}

例如

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



3> 小智..:

这个想法的一个问题是xpath"破坏"信息.

有无数个xml树可以匹配许多xpath.现在在某些情况下,就像你给出的例子一样,有一个明显的最小xml树,它匹配你的xpath,你有一个使用"="的谓词.

但是,例如,如果谓词使用不相等,或者除了相等之外的任何其他算术运算符,则存在无限多种可能性.您可以尝试选择一个"规范"的xml树,例如,需要最少的位来表示.

假设您有xpath /feed/entry/content[@source > 0].现在任何适当结构的xml树,其中节点内容具有值> 0的属性源将匹配,但是存在大于零的无限数量的数字.通过选择"最小"值(大概为1),您可以尝试规范化xml.

Xpath谓词可以包含非常任意的算术表达式,因此对此的一般解决方案即使不是不可能也是非常困难的.你可以想象那里有一个巨大的等式,它必须反过来解决,才能得出与这个等式匹配的数值; 但由于可能存在无限数量的匹配值(只要它实际上是不等式而不是等式),就需要找到规范的解决方案.

其他形式的许多表达也会破坏信息.例如,像"或"这样的运营商总是破坏信息.如果你知道(X or Y) == 1,你不知道X是1,Y是1,还是两者都是1; 所有你肯定知道的是其中一个是1!因此,如果你有一个使用OR的表达式,你无法分辨哪个节点或输入到OR的值应该是1(你可以做出任意选择并将它们都设置为1,因为这将满足表达式,因为两种选择,其中只有一种是1).

现在假设xpath中有几个表达式引用同一组值.然后,你最终会得到一个几乎不可能解决的联立方程或不等式系统.同样,如果将允许的xpath限制为其全部功率的一小部分,则可以解决此问题.我怀疑完全一般的情况类似于图灵停止问题; 在这种情况下,给定一个任意程序(xpath),找出一组与程序匹配的一致数据,并且在某种意义上是最小的.



4> Eranga Dissa..:

这是我的版本.希望这也会对某人有所帮助.

    public static void Main(string[] args)
    {

        XmlDocument doc = new XmlDocument();
        XmlNode rootNode = GenerateXPathXmlElements(doc, "/RootNode/FirstChild/SecondChild/ThirdChild");

        Console.Write(rootNode.OuterXml);

    }

    private static XmlDocument GenerateXPathXmlElements(XmlDocument xmlDocument, string xpath)
    {
        XmlNode parentNode = xmlDocument;

        if (xmlDocument != null && !string.IsNullOrEmpty(xpath))
        {
            string[] partsOfXPath = xpath.Split('/');


            string xPathSoFar = string.Empty;

            foreach (string xPathElement in partsOfXPath)
            {
                if(string.IsNullOrEmpty(xPathElement))
                    continue;

                xPathSoFar += "/" + xPathElement.Trim();

                XmlNode childNode = xmlDocument.SelectSingleNode(xPathSoFar);
                if(childNode == null)
                {
                    childNode = xmlDocument.CreateElement(xPathElement);
                }

                parentNode.AppendChild(childNode);

                parentNode = childNode;
            }
        }

        return xmlDocument;
    }

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有