I tested with the following source xml File
<?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>
I would like to copy/clone the complete tag (room id="R1") and paste as last room within floor. At the same time I want to change the tag value of (room id="R1") to "Whatever" and the (socket id) within the (room)-Tag to "new room"
Finaly I want to get the following xml-structure
<?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>
But I have problems with that. After creating the clone I can not change the value of the new room, without deleting the nested tags. The third last comand causes my problem. If you skip that comand you get a nearly perfect clone, but I do not know how to change the value.
Here my c# Code
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");
}
}
}
How can I change the value in c# using linq, without deleting an embeded xml structure?
Thanks for your tips
You'll have to see your room
element as a node of a tree. Everything below that is a node as well, so in fact, Child1
is a node (text) and electrics
is node (element).
Visually represented:
* Element: room (with attribute id)
|- Text: Child1
|- Element: electrics
|- Element: socket (with attribute id)
So to get to that text-node (Child1
), just change the first subnode of room
like such:
newRoom1.Nodes().First().ReplaceWith("Whatever");
If you'd change .Value
of room, you'd be replacing the entire subtree of room
.
I'd avoid this kind of structure though, if possible. Change the 'Child1' value so that it is either an attribute of room (preferred), or wrap it in a node so that <room id="1"><description>Child1</description></room>