How to update all instances of an element in XDocument? - c#

I have xml structure as below. How can I replace the element value of that is present everywhere in this structure? Is there a way to do this using Linq? Also, sometimes the structure could be different but there will always be Resource element so I need to look at all instances of Resource and not care about where it is present.
Thanks for any suggestions.
<Users>
<User>
<Number>123456</Number>
<ID>1</ID>
<Events>
<Event>
<ID>12</ID>
</Event>
</Events>
<Items>
<Item>
<ID>12</ID>
<Resource>Replace this value</Resource>
</Item>
<Item>
<ID>13</ID>
<Resource>Replace this value</Resource>
</Item>
<Item>
<ID>14</ID>
<Resource>Replace this value</Resource>
</Item>
</Items>
</User>
//More User elements where Resource needs to be updated
<User>
</User>
<User>
</User>
</Users>

Linq is a query language, so you can't directly use it to modify the value, but you can easily select all the Resource elements in the document with it and iterate/change them.
For example:
// or load from xml, however you have it
var xDoc = XDocument.Load(#"c:\temp\myxml.xml");
// iterate every Resource element
foreach (XElement element in xDoc.Descendants("Resource"))
element.Value = "Hello, world";
That will pick out every Resource element in the XML regardless of where it is in the hierarchy, which in your case, is what you need. If you needed to target it more specifically, you could either use an XPath expression or further Linq calls such as Element() which work on a single level of the hierarchy.

Related

Loop each Items in c# using XElement

I am facing some difficulty in swapping the id attribute in the list of Items in a C# XDocument. The id is a reference.
Input:
<Items>
<Item base="book_bookref1_ref1">
<Name>Test1</Name>
<Ref>bookref1</Ref>
</Item>
<Item base="book_bookref1_ref2">
<Name>Test2</Name>
<Ref>bookref1</Ref>
</Item>
<Item base="book_bookref2_ref1">
<Name>Test3</Name>
<Ref>bookref2</Ref>
</Item>
<Item base="book_bookref2_ref2">
<Name>Test4</Name>
<Ref>bookref2</Ref>
</Item>
</Items>
Expected Output:
<Items>
<Item base="book_bookref1_ref1" id="book_bookref1_ref2">
<Name>Test1</Name>
<Ref>bookref1</Ref>
</Item>
<Item base="book_bookref1_ref2" id="book_bookref1_ref1">
<Name>Test2</Name>
<Ref>bookref1</Ref>
</Item>
<Item base="book_bookref2_ref1" id="book_bookref2_ref2">
<Name>Test3</Name>
<Ref>bookref2</Ref>
</Item>
<Item base="book_bookref2_ref2" id="bookref2_ref1">
<Name>Test4</Name>
<Ref>bookref2</Ref>
</Item>
</Items>
There is a tag <Ref> where one part of value ex:bookref1 is a reference for Base attribute book_bookref1_ref1 for the 1st and 2nd Item.
I need to write the 2nd 'id' to the 1st Item and write the 1st id to the 2nd Item in the loop. Similarly for the 3rd and 4th Item. There can be many but it's like a combination (1 & 2, 3 & 4, 5 & 6, etc).
The problem here is there can be multiple Items and there is no relation in the Item to map the Id attribute.
The code I'm using is loading XMLDocument into XDocument getting the Items in LINQ.
Var doc = XDocument.Load(xml)
var ItemsList
foreach(var itm in ItemsList)
{
// I'm stuck here. How do we get the id attribute value based on Ref tag?
}
Please let me know your suggestions.
Nothing in XML is impossible with a little LINQ (and MoreLinq, in this example):
var xdoc = XDocument.Parse(input);
foreach(var pair in xdoc.XPathSelectElements("Items/Item").Batch(2).ToArray())
{
pair[0].SetAttributeValue(XName.Get("id"), pair[1].Attribute(XName.Get("base")).Value);
pair[1].SetAttributeValue(XName.Get("id"), pair[0].Attribute(XName.Get("base")).Value);
}
This enumerates not each Item, not each Item pair (two elements in a batch). You only need to tweak this to decide what to do if an odd element remains at the end of the loop.
MoreLinq is for .Batch() extension method, but if you need, you can easily write it yourself.

Identifying a single XML element and calling in C#

I currently have a program(C#) I am working on that uses an XML file to keep and item and its sell and buy price. But I need to figure out how to identify and call just a single section such as the "pristine Robot Brainstorm Bulb" in my C# project.
I need to be able to call a separate section rather than the whole lot of it.
Here is my XML
<items>
<Item>
<itemName>Pristine Robot Brainstorm Bulb</itemName>
<defindex>5701</defindex>
<maxAmount>25</maxAmount>
<sellPrice>4</sellPrice>
<buyPrice>0</buyPrice>
</Item>
<Item>
<itemName>Pristine Robot Currency Digester</itemName>
<defindex>5700</defindex>
<maxAmount>25</maxAmount>
<sellPrice>4</sellPrice>
<buyPrice>0</buyPrice>
</Item>
<Item>
<itemName>reinforced robot emotion detector</itemName>
<defindex>5702</defindex>
<maxAmount>150</maxAmount>
<sellPrice>.5</sellPrice>
<buyPrice>0</buyPrice>
</Item>
<Item>
<itemName>reinforced robot humor suppression pump</itemName>
<defindex>5703</defindex>
<maxAmount>150</maxAmount>
<sellPrice>.5</sellPrice>
<buyPrice>0</buyPrice>
</Item>
Just use Linq to Xml to query for the specific node that you are looking for, based the any of the values of the child elements.
var xDoc = XDocument.Parse("<my xml>");
string itemName = "Pristine Robot Brainstorm Bulb";
var item = xDoc.Root.Elements("Item")
.FirstOrDefault(x=>x.Element("itemName").Value == itemName);
If your aim is to call them from .cs file then simply give name to your elements.
<Item x:Name = "P_R_C_Register">
<itemName>Pristine Robot Currency Digester</itemName> //and so on...
Then intellisense will show you your element when you are coding C#. I hope I understood your problem correct.

How can I use XMLReader to read number of elements below specific node element?

Lets say you have the following xml convention which can't be replaced:
Each class is represented By <Class> and HashCode attribute.
The name of the class is right below the <Class> tag.
<?xml version="1.0" encoding="utf-8"?>
<Class HashCode="1">
<Person>
<Class HashCode="-3">
<FullName>
<FirstName>Dan</FirstName>
<LastName>K</LastName>
</FullName>
</Class>
<Age>20</Age>
<Class HashCode="4">
<Address>
<Street>abc</Street>
<City>new york</City>
<ZipCode>30500</ZipCode>
<PhoneNumber>1245</PhoneNumber>
</Address>
</Class>
</Person>
Question: How can I use XMLReader object in order to read the number of Node elements after a given element, until I reach an element tag which wasn't followed by <Class> tag.
For example:
Starting at <FullName> will automatically stop at <LastName> (because one tag before there is no <Class> tag) and return the number of elements between them which I guess is 1.
Assumption: Consider you have the start tag's Hashcode value and Name which will come of course one level after <Class Hashcode=> tag.
I tried many things with XMLReader methods and got complicated, was trying to convert to XMLDocument with XMlReader.ReadSubtree() but seems that XMLDocument didn't help me also.

Convert node section into one line

i have xml document with a lot of items like this:
<item>
<key>
<unsignedShort>x</unsignedShort>
</key>
<value>
<unsignedShort>y</unsignedShort>
</value>
</item>
<item>
......
now i want that every section of it will look like this:
<item><key><unsignedShort>x</unsignedShort></key><value><unsignedShort>y</unsignedShort></value></item>
You would have to implement your own XmlWriter. XmlTextWriter allows you to use an unindented format, but that would result in the entire document being on one line. So, yeah, you'd have to roll your own, unless you're OK with the entire document sitting on one line.

Flatten XML structure by element with linq to xml

I recently created a post about flattening an XML structure so every element and it's values were turned into attributes on the root element. Got some great answer and got it working. However, sad thing is that by flattening, the client meant to flatten the elements and not make them into attributes :-/
What I have is this:
<members>
<member xmlns="mynamespace" id="1" status="1">
<sensitiveData>
<notes/>
<url>someurl</url>
<altUrl/>
<date1>somedate</date1>
<date2>someotherdate</date2>
<description>some description</description>
<tags/>
<category>some category</category>
</sensitiveData>
<contacts>
<contact contactId="1">
<contactPerson>some contact person</contactPerson>
<phone/>
<mobile>mobile number</mobile>
<email>some#email.com</email>
</contact>
</kontakter>
</member>
</members>
And what I need is the following:
<members>
<member xmlns="mynamespace" id="1" status="1">
<sensitiveData/>
<notes/>
<url>someurl</url>
<altUrl/>
<date1>somedate</date1>
<date2>someotherdate</date2>
<description>some description</description>
<tags/>
<category>some category</category>
<contacts/>
<contact contactId="1"></contact>
<contactPerson>some contact person</contactPerson>
<phone/>
<mobile>mobile number</mobile>
<email>some#email.com</email>
</member>
</members>
So basically all elements, but flattened as childnodes of . I do know that it's not pretty at all to begin parsing XML documents like this, but it's basically the only option left as the CMS we're importing data to requires this flat structure and the XML document comes from an external webservice.
I started to make a recursive method for this, but I've got an odd feeling that it could be made smoother (well, as smooth as possible at least) with some LINQ to XML (?) I'm not the best at linq to xml, so I hope there's someone out there who would be helpful to give a hint on how to solve this? :-)
This seems to work - there may be neater approaches, admittedly:
var doc = XDocument.Load("test.xml");
XNamespace ns = "mynamespace";
var member = doc.Root.Element(ns + "member");
// This will *sort* of flatten, but create copies...
var descendants = member.Descendants().ToList();
// So we need to strip child elements from everywhere...
// (but only elements, not text nodes). The ToList() call
// materializes the query, so we're not removing while we're iterating.
foreach (var nested in descendants.Elements().ToList())
{
nested.Remove();
}
member.ReplaceNodes(descendants);

Categories

Resources