How to delete certain root from xml file? - c#

My '.xml' file looks this way:
<?xml version="1.0" encoding="utf-8"?>
<Requestes>
<Single_Request num="1">
<numRequest>1</numRequest>
<IDWork>1</IDWork>
<NumObject>1</NumObject>
<lvlPriority>Высокий</lvlPriority>
</Single_Request>
<Single_Request num="2">
<numRequest>2</numRequest>
<IDWork>2</IDWork>
<NumObject>2</NumObject>
<lvlPriority>Средний</lvlPriority>
</Single_Request>
<Periodic_Request num="1">
<numRequest>3</numRequest>
<IDWork>23</IDWork>
<pFrequency>23</pFrequency>
<lvlPriority>Низкий</lvlPriority>
<time_service>23</time_service>
<time_last_service>23</time_last_service>
<relative_time>23</relative_time>
</Periodic_Request>
</Requestes>
So I need to delete Single_Request with atribute value equal to sTxtBlock_numRequest.Text. I have tried to do it this way:
XDocument doc = XDocument.Load(FilePath);
IEnumerable<XElement> sRequest = doc.Root.Descendants("Single_Request").Where(
t => t.Attribute("num").Value =="sTxtBlock_numRequest.Text"); //I'm sure, that problem is here
sRequest.Remove();
doc.Save(FilePath);
Unfortunattly, nothing has happanned, don`t know how to solve the problem.
This is why , I am looking forward to your help.

You are comparing attribute value with string literal "sTxtBlock_numRequest.Text". You should pass value of textbox text instead:
doc.Root.Elements("Single_Request")
.Where(t => (string)t.Attribute("num") == sTxtBlock_numRequest.Text)
.Remove();
Note - it's better to use Elements when you are getting Single_Request elements of root, because Descendants will search whole tree, instead of looking at direct children only. Also you can call Remove() without saving query to local variable.

Related

When using C# XmlSerializer and SetElementValue on an XElement when you set it the value to null or empty string it's removed

If you open an XML Document with XDocument.Load(path) and then look through Descendants when you find the one you are looking for and use SetElementValue if you set the value to an empty string ("") or null it ends up removing the tag so when you save the document it's lost.
I want to be able to keep the tag when the value is null or an empty string. I've not been able to find a way to do this.
Is my only option to deserialize the entire XML document into objects edit those objects and write over the file rather than just loading the XmlDocument and editing it?
Sorry, it took me a while to get back to this. I found what my issue was so the code was all correct. What I hadn't noticed before was that this line was at the end before the save.
xDocument.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove();
This goes through all the descendants and finds any that are null or empty strings and removes them which was my problem.
XElement.SetElementValue(elementName, elementValue);
This does exactly as documented. When the elementValue is NULL it will remove the element but when it's an empty string it will put leave the element as an empty element in long-form, not the short form which is fine for my case.
For completeness of this answer and since those asked for example code here is some.
Sample.cfg
<?xml version="1.0" encoding="utf-8"?>
<ParentNode>
<ChildNode>
<PropertyOne>1</PropertyOne>
<PropertyTwo>Y</PropertyTwo>
</ChildNode>
<ChildNode>
<PropertyOne>2</PropertyOne>
<PropertyTwo>N</PropertyTwo>
</ChildNode>
</ParentNode>
Sample Code
// See https://aka.ms/new-console-template for more information
using System.Xml.Linq;
var xDocument = XDocument.Load("Sample.cfg");
foreach (var childNode in xDocument.Descendants("ChildNode"))
{
foreach (var element in childNode.Elements())
{
if (element.Name == "PropertyOne" && element.Value == "2")
{
childNode.SetElementValue("PropertyTwo", "");
}
// Uncomment this line to always have it remove null and empty string descendants
//xDocument.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove();
xDocument.Save("Sample.cfg");
}
}

Replace element tag keep its children

Lets say i have this simple xml:
<root>
<dad>
<kid> wee </kid>
</dad>
</root>
My goal is to replace <dad> by <mom> but keeping the kid information.
Ive tryed
xml.XPathSelectElements("dad")
then looping over and
dad.ReplaceWith(new XElement("mom", dad.descendants());
but it doesnt work :(
Any ideas on how to do this ?
It looks like you want to rename the element 'dad' to 'mom':
xml.Root.Elements("dad").ToList().ForEach(d => d.Name = "mom");
If 'mom' is an existing element with its own elements and attributes i would replace the 'dad'-element and add the 'kid'-element to the 'mom' element:
xml.Root.Elements("dad").ToList().ForEach(d =>
{
XElement mom = new XElement("mom"); //use your mom here
mom.Add(d.Descendants());
d.ReplaceWith(mom);
});

c# extract value from nextnode

I have the following xml part and am trying to extract the value where key is known. The example below is a snippet, from a larger xml that contains 1000's of nodes.
<?xml version="1.0" encoding="utf-8"?>
<DictionarySerializer>
<item>
<key>key1</key>
<value>CONTENT1</value>
</item>
<item>
<key>key2</key>
<value>CONTENT2</value>
</item>
</DictionarySerializer>
i assume the above is a string called xml,
then with
XDocument.Parse(xml)
.Descendants("key")
.Where(x => (string)x.Value == "key1")
.FirstOrDefault().NextNode.ToString()
I can get the string <value>CONTENT1</value> But i simply cannot get my head around how to get the value of the value node to to say.
I am afrad it is super simple, and i just are stuck in a coffein loop :-)
XDocument.Parse(xml)
.Descendants("key")
.Where(x => (string)x.Value == "key1")
.FirstOrDefault().Value.ToString()
you should use .Value property instead of .NextNode
If you want to get all keys and values from the XML from all 1000 elemnts. You can use:
Dictionary<string, string> elements = new Dictionary<string, string>();
xml.Root.Elements().ToList().ForEach(xmlElement =>
{
elements.Add(xmlElement.Descendants("key").First().Value,
xmlElement.Descendants("value").First().Value);
});
So, the elements dictionary will contain all of your 1000 nodes.
Try to cast NextNode to XElement and get Value from it.
Considering you can use XPath expressions.
expression = #"//Item[Key='1']/Value"
XmlNodeList nodeList = xmlDocument.SelectNodes(expression);
This would give you the value node(s) of items with Key=1. Just find the value of the desired node.
I believe using XDocument you can also try,
string output = xDocument.XPathEvaluate(expression);

How to modify XML file in c#?

<Customers>
<Customer1>
<Name>Bobby</Name>
<Age>21</Age>
<Address>Panjim</Address>
</Customer1>
<Customer2>
<Name>Peter</Name>
<Age>32</Age>
<Address>Panjim</Address>
</Customer2>
<Customer4>
<Name>Joel</Name>
<Age>32</Age>
<Address>Mapusa</Address>
</Customer4>
</Customers>
So the thing is I want to delete a particular element and when i delete the first element i.e customer1, I want to update the other elements. I mean I want to make customer3, customer2 and customer2, customer1.
Can anyone please help me achieve this?
What about:
class Program {
static void Main(string[ ] args) {
XDocument doc = XDocument.Load("D:\\file.xml"); //example file
doc.Root.SwitchAndRemove("Customer1");
doc.Save("D:\\file.xml");
}
}
public static class Utilities {
public static void SwitchAndRemove(this XElement customers, XName name) {
var x = customers.Descendants().Where(e => e.Name == name).Select((element, index) => new { element, index }).Single();
int count = 0;
XElement temp = x.element;
foreach (XElement el in customers.Nodes()) {
if (count == x.index + 1) {
temp.RemoveAll();
temp.Add(el.Descendants().ToArray());
temp = el;
}
else
count++;
}
temp.Remove();
}
}
By giving as input your xml the output is the following:
<?xml version="1.0" encoding="utf-8"?>
<Customers>
<Customer1>
<Name>Peter</Name>
<Age>32</Age>
<Address>Panjim</Address>
</Customer1>
<Customer2>
<Name>Joel</Name>
<Age>32</Age>
<Address>Mapusa</Address>
</Customer2>
</Customers>
I'd argue that your problem is not how you could rename your nodes with minimum effort but structure of your XML file.
You said order of customers is not important and apparently customer tag's number is not important, either, since you want to rename the tags upon deletion.
So maybe this structure just creates unnecessary complexity and extra work for you.
Only reason I see you could need the number in tag is to identify the node you are about to remove. Am I right or is there something more to it? If not then you could add random unique identifier (like Guid) to your customer data to remove the right one.
Could save you lot of trouble.
<customers>
<customer>
<guid>07fb-877c-...</guid>
<name>Notch</name>
<age>34</age>
<address>street</address>
</customer>
<customer>
<guid>1435-435a-...</guid>
<name>Sam</name>
<age>23</age>
<address>other</address>
</customer>
<customers>
Say the element you have to delete is Customer1, first of all you can read the complete xml file using one of the XML parsing classes available in c# like XDocument or XmlReader and write to another xml file say "Temp.xml" skipping the Customer1 element completely. This way we have achieved the deletion part.
Next to update, forget the file being XML file and read the entire file to a string, say "xmlstring". Now use the Replace function available with a string data type to replace "Customer2" with "Customer1" and then "Customer3" with "Customer2" and so on.
And now delete your original XML file and write the string "xmlstring" using a stream writer to a file name "YourFileName.xml"
Thats it. Hope this solution works for you. Try this and in case u are unable get this done, share the code which u tried and we shall suggest how to work it out.
taken from your comment that the order does not have to be preserved then you can do this
public static void RemoveCustomer(XElement customers, XElement removeThis){
var last = customeers.Elements().Last();
if(last != removeThis){
foreach(var element in removeThis.Elements()){
element.Value = last.Element(element.Name).Value;
}
}
last.Remove();
}
It effectively substitutes the one to be removed with the last (unless the last should be removed) and thereby eliminates the need for renaming any of the other elements

Xml updating with linq not working when quering

I have problem i'm trying to update a specific part of the XML with the linq query but it doesn't work. So i an xml file:
<?xml version="1.0" encoding="utf-8"?>
<DesignConfiguration>
<Design name="CSF_Packages">
<SourceFolder>C:\CSF_Packages</SourceFolder>
<DestinationFolder>C:\Documents and Settings\xxx</DestinationFolder>
<CopyLookups>True</CopyLookups>
<CopyImages>False</CopyImages>
<ImageSourceFolder>None</ImageSourceFolder>
<ImageDesinationFolder>None</ImageDesinationFolder>
</Design>
</DesignConfiguration>
I want to select the part where the part where there is Design name="somethning" and get the descendants and then update the descendants value that means this part:
<SourceFolder>C:\CSF_Packages</SourceFolder>
<DestinationFolder>C:\Documents and Settings\xxx</DestinationFolder>
<CopyLookups>True</CopyLookups>
<CopyImages>False</CopyImages>
<ImageSourceFolder>None</ImageSourceFolder>
<ImageDesinationFolder>None</ImageDesinationFolder>
I have this code:
XDocument configXml = XDocument.Load(configXMLFileName);
var updateData = configXml.Descendants("DesignConfiguration").Elements().Where(el => el.Name == "Design" &&
el.Attribute("name").Value.Equals("CSF_Packages")).FirstOrDefault();
configXml.Save(configXMLFileName);
I'm getting the null data in the updateData varibale. When I'm trying the Descendat's function through QuickWatch it also returns a null value. When I'm checking the configXML variable it has data that is my whole xml. What am I doing wrong?
Try this:
var updateData =
confixXml
.Root //Root Element
.Elements("Design") //All elements under root called Design
.Where(element => (String)element.Attribute("name") == "AFP_GRAFIKA") //Find the one with the name Attribute of AFP_GRAFIKA
.FirstOrDefault(); //Grab the first one it finds or return null.
if (updateData != null)
{
var myElements =
updateData
.Elements(); //All the elements under the Design node
}
XDocument xml = XDocument.Load("");
XElement settings = (from children in xml.Descendants("DesignConfiguration")
where children.Name.Equals("Design") && children.Attribute("name").Equals("CSF_Packages")
select children).FirstOrDefault();
settings.Element("SourceFolder").SetValue("filepath");
settings.Element("CopyImages").SetValue(true);
Ok, so I've managed to fix the problem. I don't know why but it worked. It seems that the Descendants function returns null as a stand alone function but with linq it works. So for my solution only thing what should be done is this:
var updateData = (from s in configXml.Descendants("Design")
where s.Attribute("name").Value == design.DesignName
select s).First();
At first before I sent you my question I've tried this but I didn't have the select s part. Besides when I wrote the where s.Atribute part in the curly brackets I've inserted the design.DesignName object instead of the name of the attribute. So no it works ok. Thanks for your help and everything. Til nex time. Have a nice day/night everyone :)
Because DesignConfiguration was your root node, the Descendants("DesignConfiguration) was returning null. By using the .Descendants("Design"), you were looking at child nodes, not the self.

Categories