【问题标题】:Duplicate elements when adding XElement to XDocument将 XElement 添加到 XDocument 时重复元素
【发布时间】:2010-06-09 15:12:32
【问题描述】:

我正在用 C# 编写一个程序,它将检查一堆 config.xml 文件并更新某些元素,或者如果它们不存在则添加它们。如果存在以下代码,我将更新元素的部分:

XDocument xdoc = XDocument.Parse(ReadFile(_file));
XElement element = xdoc.Elements("project").Elements("logRotator")
                        .Elements("daysToKeep").Single();
element.Value = _DoRevert;

但是当我想添加一个不存在的元素时,我遇到了问题。大多数情况下,树的一部分就位,当我使用我的代码时,它会添加另一个相同的树,这会导致读取 xml 的程序崩溃。

这是我尝试的方法

xdoc.Element("project").Add(new XElement("logRotator", new XElement("daysToKeep", _day)));

结果是这样的结构(numToKeep 标签已经存在):

<project>
  <logRotator>
    <daysToKeep>10</daysToKeep>
  </logRotator>
  <logRotator>
    <numToKeep>13</numToKeep>
  </logRotator>
</project>

但这就是我想要的

<project>
  <logRotator>
    <daysToKeep>10</daysToKeep>
    <numToKeep>13</numToKeep>
  </logRotator>
</project>

【问题讨论】:

    标签: c# xml linq linq-to-xml


    【解决方案1】:

    如果给定项目存在,则查找给定项目的 logRotator 元素,如果不存在,则创建该元素。

    // project is XElement
    XElement logRotator = project.Element("logRotator");
    
    if (logRotator == null)
    {
        logRotator = new XElement("logRotator");
        project.Add(logRotator);
    }
    
    logRotator.Add(new XElement("daysToKeep", someValue));
    

    为了可重用性(如果您有其他可能存在或不存在的元素),您可以将其提取到方法中。

    static void AddToElement(XElement outerParent, string innerParent, string name, object value)
    {
        XElement inner = outerParent.Element(innerParent);
        if (inner == null)
        {
            inner = new XElement(innerParent);
            outerParent.Add(inner);
        }
    
        inner.Add(new XElement(name, value));
    }
    

    简单测试

    string xml = @"<project> 
                        <logRotator> 
                        <numToKeep>13</numToKeep> 
                        </logRotator> 
                    </project>";
    
    XDocument document = XDocument.Parse(xml);
    XElement project = document.Element("project");
    AddToElement(project, "logRotator", "daysToKeep", 10);
    
    Console.WriteLine(document.ToString());
    

    【讨论】:

    • 这适用于树的深度不超过两层的情况。我对正在发生的事情有点模糊,但我正在尝试看看我是否可以调整它以处理 n 级深的元素。感谢您迄今为止的帮助
    【解决方案2】:

    如果numToKeep 已经存在,试试这个:

    xdoc.Element("project")
        .Element("logRotator")
        .Add(new XElement("daysToKeep", _day));
    

    否则,当logRotator 不存在时添加整行,使用:

    xdoc.Element("project").Add(
        new XElement("logRotator", 
            new XElement("daysToKeep", _day),
            new XElement("numToKeep", _num)
    ));
    

    XElement.Add(Object[])

    【讨论】:

    • 这就是我的想法,但有时树更深,有时它可能会以不同的长度被“切断”,我只是假设不会为每种可能性写一个案例。
    • @Andy:您能否添加一个 xpath 查询来一次获取所有 logRotator 对象...然后检查它们是否有 daysToKeep 和 numToKeep 子对象...如果没有...添加它们。如果是这样...移动到下一个 logRotator。
    • @Scott:他(和我们)的代码使用了.Element() 方法来获取第一个元素,因此我们可以放心地假设它只用于单个元素。
    【解决方案3】:

    在每个人的启动下,我最终做到了这一点,并且成功了。它远非优雅,但它完成了工作

    /// <summary>
    /// adds an element to an xml tree and builds the tree up if it is needed
    /// </summary>
    /// <param name="outerParent">root node</param>
    /// <param name="innerPath">root/childOne/innerParent/name</param>
    /// <param name="name">element to add</param>
    /// <param name="value">elements value</param>
    static XElement BuildTree(XElement outerParent, string innerParentPath, string name, object value)
    {
        List<string> s = innerParentPath.Split('/').ToList(); 
        string str = "";
        XElement prevInner = null;
        if (s.Count != 2)//use 2 since we know the root will always be there
        {
            var t = new List<string>(s);
            t.RemoveRange(s.Count - 1, 1);//remove last element
            string[] sa = t.ToArray();
            str = string.Join("/", sa);
            prevInner = BuildTree(outerParent, str, name, value);//call recursively till we get to root;
        }
        else
        {
            prevInner = outerParent;
        }
        XElement inner = prevInner.Element(s[s.Count - 1]);
        if (inner == null)
        {
            if (s[s.Count - 1] == name)//add actual element if we're at top lvl.
            {
                prevInner.Add(new XElement(name, value));
            }
            else
            {
                inner = new XElement(s[s.Count - 1]);
                prevInner.Add(inner);
            }
        }
        return inner;
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 2016-08-27
      • 2012-11-07
      相关资源
      最近更新 更多