Loop each Items in c# using XElement - c#

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.

Related

How to update all instances of an element in XDocument?

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.

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.

XML Parsing - Removing nodes and keeping the overall structure

so I have a massive XML file that contains a structure similar to this one:
<Item ItemId=";ResTVersion" ItemType="0" PsrId="245" Leaf="false">
<Disp Icon="Str" Expand="true" Disp="true" LocTbl="false" Order="13352" />
<Modified By="sachink" DateTime="2008-12-16T19:02:35Z" />
<PsrProps>
<Str Name="Kii" Val="yyyyyyyyyyyyy" />
</PsrProps>
<Item ItemId=";ResTFileVersion" ItemType="0;ResT" PsrId="245" InstFlg="true" Leaf="true">
<Str Cat="Text" UsrLk="true">
<Val><![CDATA[ttttttttt]]></Val>
<Tgt Cat="Text" Orig="New">
<Val><![CDATA[ttttttttt]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" Order="13353" />
<Modified By="sachink" DateTime="2008-12-16T19:02:35Z" />
<Cmts>
<Cmt Name="Dev"><![CDATA[{Locked}]]></Cmt>
</Cmts>
</Item>
<Item ItemId=";ResTLanguageTag" ItemType="0;ResT" PsrId="245" InstFlg="true" Leaf="true">
<Str Cat="Text" UsrLk="true">
<Val><![CDATA[en-US]]></Val>
<Tgt Cat="Text" Orig="New">
<Val><![CDATA[en-US]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" Order="13354" />
<Modified By="sachink" DateTime="2008-12-16T19:02:35Z" />
<Cmts>
<Cmt Name="Dev"><![CDATA[=.ABVUDHUIDSHFUIDSHFUISHDFUIDSH iusdhfUIHAs]]></Cmt>
</Cmts>
</Item>
</Item>
I have several item ids and I want to create a new xml that respects the old structure.
I use this code to retrieve the nodes that I want, and then create the new XML.
XmlNodeList nodes = originalXML.SelectNodes("//*[contains(#ItemId,'" + id + "')]");
So what I want is to remove some nodes but I only have the ids of the ones I want to keep.
The problem is how do you keep the outer structure of an xml, when you use the selectnodes function, to get the inner nodes?
Thanks!
I would go the opposite way: remove what you don't need. It's hard to build an XmlDocument from scratch (takes a lot of coding).
I think you will be better off removing the nodes you don't want from the structure you already have.
XmlNodeList nodes = originalXML.SelectNodes("/*[not(contains(#test,'test'))]")
foreach(XmlNode node in nodes)
originalXML.RemoveChild(node);
should work.
If you need to preserve your original structure you can originalXML.Clone() it.
Sidenote: You might want to look into System.Xml.Linq.XDocument and System.Xml.Linq.XElement I find those a lot easier to use.

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.

xml serialization and comments

I'm working on some custom xml editor. I have a library that contains all the classes that can be de/serialized. The problem is that during deserialization all the comments that were in the xml are lost. Let's say I have a xml structure:
<mainNode>
<subNode name="firstSubNode">
<item prop1="a" prop2="b"/>
<item prop1="c" prop2="e"/>
<!--Here I have important comment-->
<item prop1="d" prop2="f"/>
</subNode>
<!--and also here I have a comment-->
<subNode name="secondSubNode">
<item prop1="a" prop2="b"/>
<item prop1="c" prop2="e"/>
</subNode>
</mainNode>
Now when I deserialize such xml file I will have a main object (mainNode) that contains two subNodes with items. Now if I serialize this object, the result will be the same file but without comments.
The question: is there a way to keep this comments when running serialize/deserialize method? If yes how will this work when I add/remove/edit some nodes/items that contain comments.

Categories

Resources