LINQ to XML Replace Value Issue - c#

I am having an issue when trying to replace a value of an XElement. I am not sure what I am doing wrong. My simplified xml is as follows
<payload>
<field name="SLUG_LOCATOR">
<value>ORIG VALUE</value>
</field>
</payload>
Here is my query
IEnumerable<XElement> fields =
from el in doc.Descendants("field")
where el.Attribute("name").Value == "SLUG_LOCATOR"
select el;
foreach (XElement el in fields)
{
e.Value = "NEW VALUE";
}
If I check el in the for each statement it says that the Value = ORIG VALUE. So I assumed I could just change that and I would expect to see something like this
<payload>
<field name="SLUG_LOCATOR">
<value>NEW VALUE</value>
</field>
</payload>
But this is what I get and I dont know why
<payload>
<field name="SLUG_LOCATOR">NEW VALUE</field>
</payload>
Any help would be greatly appreciated!

Currently, you're selecting <field> element instead of <value> element. That's why the entire content of <field> get changed to NEW VALUE. You can simply modify the select part of your LINQ for the slightest change to fix the problem :
IEnumerable<XElement> values =
from el in doc.Descendants("field")
where el.Attribute("name").Value == "SLUG_LOCATOR"
select el.Element("value");
Or if you need to remain selecting <field>s, then change the for loop content to :
foreach (XElement el in fields)
{
el.Element("value").Value = "NEW VALUE";
}

You're getting the elements that contain the attribute. Try this to get the value subelements:
select el.Descendants("value");

Related

Formatting an elements text using descendants of XElement

Im wondering how to select a specific element from the heirarchy so that I can format its text.
In the example below I would like to format the specific element to remove the time portion of the date, but I am also looking for a way to format any of the elements for example to add a currency symbol to the text between each price tag.
My example
<orders>
<order>
<type> tools </type> //I would like the ability to select this element
<text> screwdriver </text>
<id> 100981 </id>
<price> 5.00 </price>
<date> 01/01/15 12:51:36 </date>
</order>
<order>
<type> uniform </type>
<text> boots </text>
<id> 100546 </id>
<price> 25.00 </price>
<date> 12/01/15 15:30:41 </date>
</order>
</orders>
What I have so far
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );// set the first letter of each output to be uppercase
}
}
What I have tried
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
if( element.Descendants() == element.Element("date"))
{
element.Value = Convert.ToDateTime(element.Value).ToShortDateString();
}
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );
}
}
I have some XML experience but have never worked with XElement before.
I've been searching SO for a while now but cant find what Im looking for. The answers below are some of the suggested answers from typing this question but they dont provide a solution as the XML elements are generated dynamically in a loop.
XElement node with text node has its formatting ignored
string.Format in XElement not formatting
Any help with this would be great as I have not attempted this before. Thanks.
You can get the text node's parent using Parent property and check it's name:
foreach (XText node in doc.DescendantNodes()
.Where(x => NodeType == XmlNodeType.Text))
{
if(node.Parent.Name == "date") { ... }
if(node.Parent.Name == "price") { ... }
}
BTW, don't forget the save the document using XDocument.Save method after you made the changes.
I usually create one class to represent the xml and send yo the instance of object with correct type. for Example convert.ToDecimal(node.innerText)

Reading data from XML with attributes

I have an XML file which is like below:
<CPageDataXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<control id="busRowOAppr2EIDLookUpUserControl" controltype="business">
<field controlvaluetype="single" key="busRowOAppr2EIDLookUpUserControl_txtEID">
<valuefield value="709227">E8 - John Doe</valuefield>
</field>
<field controlvaluetype="hidden_single" key="busRowOAppr2EIDLookUpUserControl_txtEID_Email">
<valuefield value="_JohnDoe#Wonder.com">emailid</valuefield>
</field>
</control>
<control id="busDelegationFromDate123" controltype="business">
<field controlvaluetype="single" key="txtCalanderDateWithImage_UserControl">
<valuefield value="" />
</field>
</control>
</CPageDataXML>
I want to read the value of the valuefield where control id="busRowOAppr2EIDLookUpUserControl"
The C# code is:
This is the code for loading the XML:
XmlDocument xPagedata=new XmlDocument();
XmlNode xnodePagedata = null;
xPagedata.LoadXml(strPageData);
This is the code for SelectSingleNode:
string a = xnodePagedata.SelectSingleNode(//Control[#id='busRowOAppr2EIDLookUpUserControl']).Attributes["Value"].Value;
I have tried to use SelectSingleNode(string) but that is giving me a null reference exception. Kindly suggest how should I go about this one. I am an absolute beginer on XML.
One possible way using the same approach :
string a =
xnodePagedata.SelectSingleNode("//control[#id='busRowOAppr2EIDLookUpUserControl']/field/valuefield/#value")
.Value;
UPDATE :
In case there are multiple <valuefield> in one <control> and you want all values, use SelectNodes() for example :
var values =
xPagedata.SelectNodes("//control[#id='busRowOAppr2EIDLookUpUserControl']/field/valuefield/#value");
foreach (XmlNode value in values)
{
Console.WriteLine(value.Value);
}
You can use XDocument : use Descendants("control") to get all controls then filter them using the Where clause then use SelectMany to get a flattened collection of values of valuefield.
XDocument doc = XDocument.Load(filepath);
var result = doc.Descendants("control")
.Where(i => (string)i.Attribute("id") == "busRowOAppr2EIDLookUpUserControl")
.SelectMany(i => i.Descendants("valuefield")
.Select(j => j.Attribute("value")))
.ToList();
And this is the result:
result Count = 2
[0] {value="709227"}
[1] {value="_JohnDoe#Wonder.com"}

XML node is missing when empty

I have an XMLDocument as result of query.
I would like to extract the <Property> value and the appropriate <Notes> for each entry.
<?xml version="1.0"?>
<EADATA version="1.0" exporter="Enterprise Architect">
<Dataset_0>
<Data>
<Row>
<PropertyID>439</PropertyID>
<Object_ID>683</Object_ID>
<Property>tagged value</Property>
<ea_guid>{5BF3E019-277B-45c2-B2DE-1887A90C6944}</ea_guid>
</Row>
<Row>
<PropertyID>444</PropertyID>
<Object_ID>683</Object_ID>
<Property>Another Tagged value</Property>
<Notes>Another tagged value notes.</Notes>
<ea_guid>{42BE8BAA-06B8-4822-B79A-59F653C44453}</ea_guid>
</Row>
</Data>
</Dataset_0>
</EADATA>
However, If the <Notes> is empty there is no <Notes> tag at all.
What XPath should I write in such cases?
Which value do you want if there is no Notes element, a null, an empty string?
I would select the Row elements with SelectNodes and then check whether a Notes child exists and assign null (as done below) or the empty string if not:
foreach (XmlElement row in doc.SelectNodes("//Row"))
{
string prop = row.SelectSingleNode("Property").InnerText;
string notes = row.SelectSingleNode("Notes") != null ? row.SelectSingleNode("Notes").InnerText : null;
}
Try this :
XPathDocument docNav = new XPathDocument(new StringReader(xml));
XPathNavigator navigator = docNav.CreateNavigator();
XPathNodeIterator NodeIter = navigator.Select("/EADATA/Dataset_0/Data/Row");
foreach (XPathNavigator selectedNode in NodeIter)
{
var a= "<root>" + selectedNode.InnerXml + "</root>";
var x= XDocument.Parse(a);
Console.WriteLine (x.Root.Element("Property").Value);
if (x.Root.Element("Notes")!=null)
Console.WriteLine (x.Root.Element("Notes").Value);
}
result :
tagged value
Another Tagged value
Another tagged value notes.

Finding a specific xml element containt

Ihave a large xml file and I want to get child element value by giving the parent child element value, I am new in xml file please any help here is my xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<masterController> <uuid>XXXXXXXXXXXXXXXXXXXXXXXXX</uuid>
<channels>
<channel>
<nodeGroups>
<nodeGroup>
<analogNode>
<typeCode>8</typeCode>
<id>1</id>
<sdos>
<sdo>
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<datafield xmlns:xsi="http://www.XXXXX.XXXX/XXXXX/XMLSchema-instance"
xsi:type="intField">
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<offset>2</offset>
<size>1</size>
<readonly>true</readonly>
<isMappedToPdo>false</isMappedToPdo>
<ownerNodeSerial>12102904</ownerNodeSerial>
<ownerSdoIndex>3</ownerSdoIndex>
<data xsi:type="intData">
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">2</value>
<unit></unit>
<min>1</min>
<max>15</max>
</data>
<intValue>2</intValue>
</datafield>
<index>3</index>
<totalbytes>3</totalbytes>
</sdo>
<sdo>
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<datafield xmlns:xsi="http://www.XXXXX.XXXX/XXXXX/XMLSchema-instance"
xsi:type="intField">
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<offset>2</offset>
<size>1</size>
<readonly>true</readonly>
<isMappedToPdo>false</isMappedToPdo>
<ownerNodeSerial>12102905</ownerNodeSerial>
<ownerSdoIndex>4</ownerSdoIndex>
<data xsi:type="intData">
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">16</value>
<unit></unit>
<min>1</min>
<max>15</max>
</data>
<intValue>2</intValue>
</datafield>
<index>3</index>
<totalbytes>3</totalbytes>
</sdo>
</sdos>
</analogNode>
</nodeGroup>
</nodeGroups>
</channel> </channels> </masterController>
I' am trying this but am not geting anything:
XElement root = XElement.Load(Server.MapPath("sample.xml"));
IEnumerable<XElement> masterco = from el in root.Elements("sdo") where (from add in el.Elements("datafield")
where
(string)add.Element("ownerNodeSerial") == TextBox1.Text &&
(string)add.Element("ownerSdoIndex") == TextBox1.Text
select add)
.Any()
select el;
foreach (XElement el in masterco)
{
TextBox3.Text = (string)el.Element("value");
}
I want to get this:
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">16</value>
and be able to update it.
There is one major error in your query:
You are using Elements on root, but you are looking for the tag sdo which is not a direct child of the root tag. You have to use Descendants instead.
Additionally, I think you want to have an OR instead of an AND regarding the text of TextBox1.
Fix it:
var masterco = from el in root.Descendants("sdo")
where (from add in el.Elements("datafield")
where
(string)add.Element("ownerNodeSerial") == TextBox1.Text ||
(string)add.Element("ownerSdoIndex") == TextBox1.Text
select add).Any()
select el;
To actually get the value you want, you should use a different query. There is really no need to select the sdo tag at all.
var value = root.Descendants("datafield")
.Where(x => (string)x.Element("ownerNodeSerial") == TextBox1.Text ||
(string)x.Element("ownerSdoIndex") == TextBox1.Text)
.Select(x => (string)x.Element("data").Element("value"))
.Single();
TextBox3.Text = value;
You can see that I am assuming that in the whole XML document only one matching datafield/data/value entry exists. I derive that information from the way you update your textbox. This would make no sense if there would be multiple tags - the values would overwrite each other in the text box.

Convert XML to a list of something

I have a xml file coming from my php site as an api.
This is the xml that is coming back from my php application.
<xml>
<overzicht>
<item>
<sessieID>6</sessieID>
<onderwerp>Vrijwilligers, een uitstervend rasnn</onderwerp>
<omschrijving>Ode aan de vrijwilligers jjj</omschrijving>
<sprekerID>1</sprekerID>
<lokaalID>20</lokaalID>
<themaID>1</themaID>
<typeID>2</typeID>
<periodeID>2</periodeID>
<datum>2012-02-20</datum>
<maximaleInschrijvingen>1</maximaleInschrijvingen>
<spreker>
<sprekerID>1</sprekerID>
<sprekerNaam>Rik Torfs</sprekerNaam>
<loginID>13</loginID>
</spreker>
<lokaal>
<lokaalID>20</lokaalID>
<campusNaam>Malle</campusNaam>
<lokaalOpCampus>W10</lokaalOpCampus>
<typeID>2</typeID>
</lokaal>
</item>
<item>
<sessieID>15</sessieID>
<onderwerp>VPKB</onderwerp>
<omschrijving/>
<sprekerID>6</sprekerID>
<lokaalID>2</lokaalID>
<themaID>1</themaID>
<typeID>1</typeID>
<periodeID>2</periodeID>
<datum>2012-02-20</datum>
<maximaleInschrijvingen>50</maximaleInschrijvingen>
<spreker>
<sprekerID>6</sprekerID>
<sprekerNaam>Dick Wursten</sprekerNaam>
<loginID>18</loginID>
</spreker>
<lokaal>
<lokaalID>2</lokaalID>
<campusNaam>KHK Vorselaar</campusNaam>
<lokaalOpCampus>A102</lokaalOpCampus>
<typeID>1</typeID>
</lokaal>
</item>
...
</overzicht>
</xml>
This is my C# code. I want to get al list of Sessie.
XDocument xmlDoc = XDocument.Parse(e.Result);
List<Sessie> sessies =
(
from item in xmlDoc.Descendants("overzicht")
select new Sessie(
item.Element("onderwerp").Value,
Convert.ToInt32(item.Element("sessieID").Value),
item.Element("omschrijving").Value,
(Spreker)(
new Spreker(
Convert.ToInt32(item.Element("spreker").Element("sprekerID").Value),
item.Element("spreker").Element("sprekernaam").Value)
),
Convert.ToDateTime(item.Element("datum").Value),
Convert.ToInt32(item.Element("maximaleInschrijvingen").Value),
(Lokaal)(
new Lokaal(
Convert.ToInt32(item.Element("lokaal").Element("lokaalID").Value),
item.Element("lokaal").Element("campusNaam").Value,
item.Element("lokaal").Element("lokaalOpCampus").Value)
)
)
).ToList<Sessie>();
I know my code isn't working with this exception.
"NullReferenceException"
There's one fairly obvious problem to start with. Look at the very start of your query:
from item in xmlDoc.Descendants("overzicht")
select new Sessie(item.Element("onderwerp").Value,
...
That will only work if there's an <onderwerp> directly under <overzicht>. There isn't - it's under the <item> element. Perhaps (given the range variable name) you meant:
from item in xmlDoc.Descendants("item")
select new Sessie(item.Element("onderwerp").Value,
...
The query
from item in xmlDoc.Descendants("overzicht")
will return a list of <overzicht> elements. item.Element("onderwerp") does not exist, you are missing the <item> element in between.
Simple fix:
from item in xmlDoc.Descendants("item")

Categories

Resources