Retrieving a value from XML based on query parameter? - c#

I have the following XML file, generated by my Windows Phone 8 app.
All works so far, but now I'd like to be able to query the file, and retrieve the associated with a particular -- So if I query it against "2222 it" would return "Location Name".
I have tried a few ways of doing this but I realised i didnt have a clue how to achieve this, even after hours of research. XML and LINQ confuse me immeasurably....so I woul really appreciate any tutorials or tips anyone could offer! Code thus far is below the sample XML document.
Thank you very much
<?xml version="1.0" encoding="utf-8"?>
<SmartSafe>
<Tag>
<tag>22222</tag>
<name>Location Name</name>
<latitude>xx.xxxxxxx</latitude>
<longitude>xx.xxxxxxx</longitude>
</Tag>
<Tag>
<tag>4444</tag>
<name>Location Name</name>
<latitude>xx.xxxxxxx</latitude>
<longitude>xx.xxxxxxx</longitude>
</Tag>
<Tag>
<tag>4444</tag>
<name>Location Name</name>
<latitude>xx.xxxxxxx</latitude>
<longitude>xx.xxxxxxx</longitude>
</Tag>
<Tag>
<tag>4444</tag>
<name>Location Name</name>
<latitude>xx.xxxxxxx</latitude>
<longitude>xx.xxxxxxx</longitude>
</Tag>
</SmartSafe>
Below is the section of the .cs which handles parsing the uid passed from the UriAssociationMapper.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("uid"))
{
uid = int.Parse(NavigationContext.QueryString["uid"]);
}
base.OnNavigatedTo(e);
string stringUid = uid.ToString();
tagID.Text = stringUid;
//Retrieve tag name from xml
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
XDocument document;
using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
if (storage.FileExists("/tagRegistry.xml"))
{
using (var stream = storage.OpenFile("/tagRegistry.xml", FileMode.Open))
{
document = XDocument.Load(stream);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
Any help would be very much appreciated thank you!!

Your LINQ to XML query should look like that:
var value = doc.Root.Elements("Tag")
.FirstOrDefault(t => (string)t.Element("tag") == "22222")
.Element("name").Value;
However, It will throw NullReferenceException if element is not found, so you shouldn't use it if you're not sure XML document contains what you're looking for. If you're not sure about that, you should probably use following instead:
var tag = doc.Root.Elements("Tag")
.FirstOrDefault(t => (string)t.Element("tag") == "22222");
var value = tag == null ? string.Empty : (string)tag.Element("name");

Related

Avoid duplicate entry in xml file c#

I am trying to restrict duplicate entry to an XML file and below is the XML file.
<?xml version="1.0" standalone="yes"?>
<Info>
<Details>
<ID>Ryan</ID>
</Details>
<Details>
<ID>Danny</ID>
</Details>
</Info>
Now if I try to add Ryan or Danny again to the ID I should alert like user name already exists.
I'm using the below code and it doesn't work. strName is a string and has username value to be added. Can anyone provide suggestions?
XDocument xDoc = XDocument.Load(Server.MapPath("~/Info.xml"));
bool userExistsAlready = xDoc.Descendants("Details").Any(x => (string)x.Attribute("ID") == strName);
if (userExistsAlready)
{
//alert
}
Try this way:
bool userExistsAlready = xDoc.Descendants("Details")
.Elements("ID")
.Any(x => x.Value == "Ryan");
The problem with your code is that it tries to access attribute ID. But ID is in fact another XML element contained inside element <Details>.
You could set ID as an attribute of Details and then check if that entry exist using the XmlDocument method GetElementByID, or implement a for cycle that checks out the property InnerText of every element in the array resulting from the call to GetElementsByName method.

Locating a value in XML

I have an xml file loaded into an XDocument that I need to extract a value from, and I'm not sure of the best way to do it. Most of the things I'm coming up with seem to be overkill or don't make good use of xml rules. I have the following snippet of xml:
<entry>
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.6.2.12" />
<code code="121070" codeSystem="1.2.840.10008.2.16.4" codeSystemName="DCM" displayName="Findings">
</code>
<value xsi:type="ED">
<reference value="#121071">
</reference>
</value>
</observation>
</entry>
There can be any number of <entry> nodes, and they will all follow a similar pattern. The value under the root attribute on the templateId element contains a known UID that identifies this entry as the one I want. I need to get the reference value.
My thought is to find the correct templateID node, back out to the observation node, find <valuexsi:type="ED"> and then get the reference value. This seems overly complex, and I am wondering if there is another way to do this?
EDIT
The xml I receive can sometimes have xml nested under the same node name. In other words, <observation> may be located under another node named <observation>.
You have problems, because your document uses Namespaces, and your query is missing them.
First of all, you have to find xsi namespace declaration somewhere in your XML (probably in the most top element).
It will look like that:
xmlns:xsi="http://test.namespace"
The, take the namespace Uri and create XNamespace instance according to it's value:
var xsi = XNamespace.Get("http://test.namespace");
And use that xsi variable within your query:
var query = from o in xdoc.Root.Element("entries").Elements("entry").Elements("observation")
let tId = o.Element("templateId")
where tId != null && (string)tId.Attribute("root") == "2.16.840.1.113883.10.20.6.2.12"
let v = o.Element("value")
where v != null && (string)v.Attribute(xsi + "type") != null
let r = v.Element("reference")
where r != null
select (string)r.Attribute("value");
var result = query.FirstOrDefault();
I have tested it for following XML structure:
<root xmlns:xsi="http://test.namespace">
<entries>
<entry>
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.6.2.12" />
<code code="121070" codeSystem="1.2.840.10008.2.16.4" codeSystemName="DCM" displayName="Findings">
</code>
<value xsi:type="ED">
<reference value="#121071">
</reference>
</value>
</observation>
</entry>
</entries>
</root>
The query returns #121071 for it.
For your input XML you will probably have to change first line of query:
from o in xdoc.Root.Element("entries").Elements("entry").Elements("observation")
to match <observation> elements from your XML structure.
Would something along the lines of the following help?
XDocument xdoc = GetYourDocumentHere();
var obsvlookfor =
xdoc.Root.Descendants("observation")
.SingleOrDefault(el =>
el.Element("templateId")
.Attribute("root").Value == "root value to look for");
if (obsvlookfor != null)
{
var reference = obsvlookfor
.Element("value")
.Element("reference").Attribute("value").Value;
}
My thought is as follows:
Pull out all the observation elements in the document
Find the only one (or null) where the observation's templateId element has a root attribute you're looking for
If you find that observation element, pull out the value attribute against the reference element which is under the value element.
You might have to include the Namespace in your LINQ. To retrieve that you would do something like this:
XNamespace ns = xdoc.Root.GetDefaultNamespace();
Then in your linq:
var obsvlookfor = xdoc.Root.Descendants(ns + "observation")
I know I had some issues retrieving data once without this. Not saying its the issue just something to keep in mind particularly if your XML file is very in depth.

looking up a value in xml C#

given this xml (just a part..)
<?xml version="1.0" encoding="utf-8"?>
<translations>
<key name="BillOfMaterials">
<translation culture="en-GB"><![CDATA[Bill of materials]]>
</translation>
<translation culture="da-DK"><![CDATA[Materiale liste]]>
</translation>
</key>
<key name="TechnicalDetails">
<translation culture="en-GB">
<![CDATA[Technical details
]]>
</translation>
</key>
..
..
...i'm looking for the simplest solution to look up for instance:
so
string thisTranslation = GetTranslation("BillOfMaterials","en-GB"); //value gets to be "Bill of materials"
I have tried the linq way, but it gets rather messy with too many itherations... especially when a simple xpath is enough in xslt... But I can't seem to just do that
Thanks in advance
Edit:
- xml is physical file
- function may not find anything.....should then just return the original key name
/translations/key[#name="BillOfMaterials"]/translation[#culture="en-GB"]
is the xpath that elsewhere is usable..
I would still use LINQ to XML - there's absolutely no need for it to get messy:
XDocument doc = XDocument.Load("test.xml");
string key = "BillOfMaterials";
var element = doc.Root.Elements("key")
.Where(key => key.Attribute("name").Value == key)
.Elements("translation")
.Where(tr => tr.Attribute("culture").Value == "en-GB")
.FirstOrDefault();
string result = (string) element ?? key;
Personally I find that cleaner than using XPath (even though the XPath is undeniably shorter). It separates each section of the query more distinctly, and also if you need any namespace handling, that would be significantly simpler with LINQ to XML than messing around with namespace managers.
You can use XPathSelectElement extension method set on XElement with your XPath selector:
Load your XML into an XDocument:
var doc = XDocument.Load("path\to\file");
and then search it with XPath:
var translation = (string)doc.XPathSelectElement(string.format("/translations/key[#name=\"{0}\"]/translation[#culture=\"{1}\"]", key, culture));
if(string.IsNullOrEmpty(translation))
translation = key;
return translation;
Get you Xml document in a XmlDocument type. You can do this like:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("<xml>"); // Can be a XML in a string, or filename, etc.
Then you can use XPath with the SelectNodes() and SelectNode() methods.
Like:
XmlNodeList xmlNodes = xmlDoc.SelectNodes("/translations/key[#name=\"BillOfMaterials\"]/translation[#culture=\"en-GB\"]");

Load info from xml, save related (changed) info using c#

Friends,
My school project is having an xml data file:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>Somewhere</add>
<mobile>0000</mobile>
.
.
.
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
My Windowsform "EditPatients_Load" is able to fetch all info of patient Jhon, and now let's assume that the Admin needs to change some information in the form & resubmit.
Then how to write back all values to Jhon's account in the same xml
file????
I'm not able to makeup the logical code, even if I check the node if (patients.paptient.name = "nameComboBox.text").... how to make sure that I'm writing other values on proper place?
Rgrdz,
Try this:
//string xml =
//#"<patients><patient><regNo>2012/Mar/003</regNo><name>Jhon</name><add>Somewhere
//</add><mobile>0000</mobile><stay>2</stay><costofroom>100</costofroom><total>200</total>
//</patient></patients>";
XDocument xmlDoc = XDocument.Load(#"c:\abc.xml");
var items = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item);
if (items.Count() > 0)
{
var item = items.First();
item.SetElementValue("add", "New New Address");
xmlDoc.Save(#"c:\abc.xml", SaveOptions.None);
}
You can get single element using
var item = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item).FirstOrDefault();
then update it using SetElementValue() method.
//Updated Xml
<?xml version="1.0" encoding="utf-8"?>
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>New Address</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
Reference:
Update XML with C# using Linq
I would take the xml serialization/deserialization route to solve this:
http://support.microsoft.com/kb/815813
How to Deserialize XML document
That way you can work with objects and not have to parse xml files manually.
If you're using .NET 3.5 onward you can use the XDocument class like the following. I'm assuming your content is in a .xml file.
XDocument xdoc = XDocument.Load(#"C:\Tmp\test.xml");
//this would ensure you get the right node and set its text content/value
xdoc.Element("patients")
.Element("patient").Element("add").Value = "some new address?";
xdoc.Save(#"C:\Tmp\test.xml");
The file test.xml would change to:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>some new address?</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>

Regarding C# Xml Reading

I'm currently doing a XML file that includes the "name" of the city, the "region", "lat" latitude and "lng".
Here is my Code:
XmlDocument XmlFile = new XmlDocument();
try {
XmlFile.Load("..\\..\\liste.xml");
}
catch (Exception ex)
{
Console.WriteLine("Erreur" + ex.Message);
};
XmlNodeList MyNodeXML = XmlFile.GetElementsByTagName("city");
foreach (XmlNode unNode in MyNodeXML)
{
string nomVille = unNode.Attributes[0].Value;
string lat = unNode.Attributes[1].Value;
string lng = unNode.Attributes[2].Value;
listeCooVilles.Add(nomVille, new PointF(float.Parse(lat), float.Parse(lng)));
}
Where listeCooVilles is a Dictionnary.
Here is my XML: I did a sample for test:
<?xml version="1.0" encoding="UTF-8"?>
<cities>
<city>
<name>Abercorn</name>
<region>Montérégie</region>
<lat>45.032999</lat>
<lng>-72.663057</lng>
</city>
<cities>
I saw many post doing the same as above in StackOverflow, but I still get an IndexOutOfRange Exception on the line
string nomVille = unNode.Attributes[0].Value;
Can someone help? Thanks!
The element has no attributes - only sub-elements. Attributes are name=value pairs at the same level as the element. E.g.
<?xml version="1.0" encoding="UTF-8"?>
<cities>
<city name="Abercorn" region="Montérégie" lat="45.032999" lng="-72.663057" />
<city name="Granby" region="Montérégie" lat="45.4" lng="-72.733333" />
</cites>
Nesting elements (as you have done originally) and using attributes (as you've coded for) are both equally valid ways of structuring your XML document.
As pointed our those are elements not attributes. Your code needs to change to this:
nomVille = unNode.Item["name"].Value
region = unNode.Item["region"].Value
lat = unNode.Item["lat"].Value
lng = unNode.Item["lng"].Value
None of the nodes in your XML sample have attributes, which is why the collection has null elements in it.
Try changing it to:
<?xml version="1.0" encoding="UTF-8"?>
<cities>
<city testAttr = "hello!">
<name>Abercorn</name>
<region>Montérégie</region>
<lat>45.032999</lat>
<lng>-72.663057</lng>
</city>
<cities>
The addition of the testAttr should provide a valid collection in unNode.Attributes.
You are using attributes in city tag but I think you should be using xml elements.

Categories

Resources