【问题标题】:c# linq change the value of xml element which contains xml tagsc# linq 更改包含 xml 标签的 xml 元素的值
【发布时间】:2015-08-11 18:22:49
【问题描述】:

我使用以下源 xml 文件进行了测试

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
<root>
    <key name="some text" id="1"/>
    <key name="some text" id="2"/>
    <house id="H1">
        <floor id="F1">
            <room id="R1">Child1<electrics><socket id="C1S1"/></electrics></room>
            <room id="R2">Child2<electrics><socket id="C2S1"/></electrics></room>
        </floor>
    </house>
</root>

我想复制/克隆完整的标签(房间 id="R1")并粘贴为楼层内的最后一个房间。同时我想将(room id="R1")的tag值改为“Whatever”,将(room)-Tag中的(socket id)改为“new room”

最后我想得到下面的xml结构

    <?xml version="1.0" encoding="utf-8"?>
<root>
  <key name="some text" id="1" />
  <key name="some text" id="2" />
  <house id="H1">
    <floor id="F1">
      <room id="R1">Child1<electrics><socket id="C1S1" /></electrics></room>
      <room id="R2">Child2<electrics><socket id="C2S1" /></electrics></room>
      <room id="R1">Whatever<electrics><socket id="new room" /></electrics></room>
    </floor>
  </house>
</root>

但我对此有疑问。创建克隆后,我无法更改新房间的值,而不删除嵌套标签。 倒数第三个命令导致了我的问题。如果您跳过该命令,您将获得近乎完美的克隆,但我不知道如何更改该值。

这是我的 c# 代码

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Xml.Linq;

namespace linqxmlTester
{
    public partial class Form1 : Form
    {
        private XElement xmlDoc;

        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {

            xmlDoc = XElement.Load("house.xml");

            // Select rooms to clone I realy only want to selekt one room, but I have to go over rooms
            IEnumerable<XElement> newRoom = from rooms in xmlDoc.Element ("house")
                                                                .Element ("floor")
                                                                .Elements("room")

                                                    where rooms.ToString().IndexOf("C1S1") > 0

                                                    select rooms;
            // I've selected realy one room in rooms with the following I clone beheind last room
            xmlDoc.Element ("house")
                  .Element ("floor")
                  .Elements("room").Last()
                  .AddAfterSelf(newRoom.First()); // there is only one

            // This worked   :-)

            //But now I want to change socket id value (="C1S1") to something new
            //I select my last created room

            XElement newRoom1 = xmlDoc.Element("house")
                                      .Element("floor")
                                      .Elements("room").Last();

            //within the newRoom1 I search for the attribute id

            foreach (var r in newRoom1.Elements("electrics").Elements("socket"))
            {
                Debug.WriteLine("aktValue " + r.Attribute("id").Value);
                r.Attribute("id").Value = "new room";
                Debug.WriteLine("newValue " + r.Attribute("id").Value);
            }

            // this is working too :-)

            // Now I only still have to change the value of my newroom
            // it should be the following code

            Debug.WriteLine("OldValue " + newRoom1.Value); // prints only the value without the embeded xml tags

            newRoom1.SetValue("Whatever"); // this kills the complete xml structure with is embede at the side of the value

            Debug.WriteLine("NewValue " + newRoom1.Value); // prints only the newvalue without the embeded xml tags

            xmlDoc.Save("housenew.xml");
        }
}
}

如何在不删除嵌入的 xml 结构的情况下使用 linq 更改 c# 中的值?

感谢您的提示

【问题讨论】:

    标签: c# xml linq xelement setvalue


    【解决方案1】:

    您必须将room 元素视为树的一个节点。它下面的所有内容也是一个节点,所以实际上Child1 是一个节点(文本),electrics 是一个节点(元素)。

    视觉表现:

    * Element: room (with attribute id)
    |- Text: Child1
    |- Element: electrics
       |- Element: socket (with attribute id)
    

    所以要访问该文本节点 (Child1),只需像这样更改 room 的第一个子节点:

    newRoom1.Nodes().First().ReplaceWith("Whatever");
    

    如果您更改房间的.Value,您将替换room 的整个子树。

    如果可能的话,我会避免这种结构。更改 'Child1' 值,使其成为房间的属性(首选),或将其包装在节点中,以便 &lt;room id="1"&gt;&lt;description&gt;Child1&lt;/description&gt;&lt;/room&gt;

    【讨论】:

    • 谢谢。 ReplaceWith 解决了我的问题。我的示例结构只是为了简化我的问题所在。实际上,我尝试编辑由 yed 生成的 graphml 文件,但遇到了很多困难。
    【解决方案2】:

    这样选择行

     class Program
        {
            static void Main(string[] args)
            {
                string input =
                    "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>" +
                    "<root>" +
                        "<key name=\"some text\" id=\"1\"/>" +
                        "<key name=\"some text\" id=\"2\"/>" +
                        "<house id=\"H1\">" +
                            "<floor id=\"F1\">" +
                                "<room id=\"R1\">Child1<electrics><socket id=\"C1S1\"/></electrics></room>" +
                                "<room id=\"R2\">Child2<electrics><socket id=\"C2S1\"/></electrics></room>" +
                            "</floor>" +
                        "</house>" +
                    "</root>";
    
                XElement xmlDoc = XElement.Parse(input);
                XElement floor = xmlDoc.Descendants("floor").FirstOrDefault();
                XElement newR1 = new XElement(floor.Elements("room").Where(x => x.Attribute("id").Value == "R1").FirstOrDefault());
                XAttribute socket = newR1.Element("electrics").Element("socket").Attribute("id");
                socket.Value = "new room";
                floor.Add(newR1);
            }
        }
    }

    这是 XML 结果

    <root>
      <key name="some text" id="1" />
      <key name="some text" id="2" />
      <house id="H1">
        <floor id="F1">
          <room id="R1">Child1<electrics><socket id="C1S1" /></electrics></room>
          <room id="R2">Child2<electrics><socket id="C2S1" /></electrics></room>
          <room id="R1">Child1<electrics><socket id="new room" /></electrics></room>
        </floor>
      </house>
    </root>

    【讨论】:

    • 这个解决方案中的房间选择比我的要短很多。但是仍然无法更改 newR1 标签的值。如果我添加 newR1.SetValue("Whatever");在 floor.add 之前,所有结构 () 都被删除。
    • 我发布了 XML 结果,这正是您正在寻找的。​​span>
    • 这几乎是我正在寻找的解决方案。我在将第二个“Child1”的值更改为“Whatever”时遇到了问题。这可以通过 newR1.Nodes().First().ReplaceWith("Whatever"); 解决。但是您使用 ".Where(x => ......) 帮助我采用了不同的方法,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多