I have an XmlDocument object in memory, here is a sample of the data:
<terms>
<letter name="F">
<term>
<name>fascículo</name>
<translation language="en">fascicle</translation>
<definition>pequeño paquete de fibras nerviosas o musculares</definition>
</term>
(There are many terms in the actual document)
I want to be able to find a term by its name node, and then add an element as a child of the term
<terms>
<letter name="F">
<term>
<name>fascículo</name>
<translation language="en">fascicle</translation>
<definition>pequeño paquete de fibras nerviosas o musculares</definition>
<image>hi there</image>
</term>
Now I can achieve this using Xpath, find the node, then create a new node with the new values, blah blah.
But that seems all a little bit long winded in the world of linq.
This is what I have so far:
private static XmlDocument AddImages(XmlDocument termDoc)
{
XDocument xDoc = XDocument.Load(new XmlNodeReader(termDoc));
using (CsvReader csv = new CsvReader(new StreamReader("spa2engimages.csv"), false))
{
csv.ReadNextRecord();
csv.ReadNextRecord();
XElement selectedTerm;
string name, imageref;
while (csv.ReadNextRecord())
{
imageref = csv[0].ToString();
name = csv[3].ToString();
selectedTerm = xDoc.Descendants("term").Single(t => t.Descendants("name").Single().Value == name);
//now want to add a new node and save it back in to the termDoc somehow
}
}
return termDoc;
}
But I am a bit lost from there. Any ideas?
The following will add the element for you
xDoc.Descendants("term").Single(t => t.Descendants("name").Single().Value == name).Add(new XElement("image", "hi there"));
The biggest issue I see which is making this clunky is the fact you need to switch back and forward between XmlDocument and XDocument. My recommendation is if you are going to use XmlDocument then use XPath and if you want to use LINQ then use XDocument. This constant switching will kill performance and maintainability.
This is how to do it with xPath, just for clarity
termDoc.SelectSingleNode("//term[name='" + name + "']").AppendChild(imageNode);
Related
I'm trying to parse a xml file, which isn't the problem. But my xml file has multiple <name> tags. One is for a song, and the other one is for an artist. I want to exclude the artist name tag, but I can't figure out how.
Here's my code so far:
string xmlString;
using (WebClient wc = new WebClient())
{
xmlString = wc.DownloadString(#"http://ws.audioscrobbler.com/2.0/?method=album.getInfo&album=awake&artist=Dream%20Theater&api_key=b5cbf8dcef4c6acfc5698f8709841949");
}
List<XElement> alleElements = new List<XElement>();
XDocument myXMLDoc = XDocument.Parse(xmlString);
List<XElement> trackNames = myXMLDoc.Descendants("album")
.Elements("tracks")
.Elements("name")
.ToList();
foreach (XElement el in trackNames)
{
Console.WriteLine(el);
}
Console.WriteLine("-----End-----");
Console.ReadLine();
I tried using .Elements("name"); instead of Descendants, but that returns nothing at all.
Here's a small part of my xml file:
<track rank="1">
<name>6:00</name>
<url>http://www.last.fm/music/Dream+Theater/_/6:00</url>
<duration>331</duration>
<streamable fulltrack="0">0</streamable>
<artist>
<name>Dream Theater</name>
<mbid>28503ab7-8bf2-4666-a7bd-2644bfc7cb1d</mbid>
<url>http://www.last.fm/music/Dream+Theater</url>
</artist>
</track>
Is there a way to exclude the <name> tag inside the <artist> tag.
If I'm not clear enough, please let me know and I'll explain it more!
It sounds like you just want to use Elements instead of Descendants, but at the right point - it's not clear where you tried using it. I'd also recommend using ToList to make things simpler. Given the sample in the documentation, it looks like this would work and be clearer in terms of where we expect there to be multiple elements:
List<XElement> trackNames = doc.Root
.Element("tracks")
.Elements("track")
.Elements("name")
.ToList();
You can try excluding with a condition, like myXMLDoc. ... .Descendants("name").Where(x => x.Parent.Name != "artist").
I did some searching around the web and could not find the cause of my problem so I apologize if that has already been asked in another form I just did not understand.
My problem is that I am trying to parse the XML retrieved from Yahoo! Fantasy Sports but nothing seems to be working.
I have converted the XML I received (using a GET request with my credentials) into a string. Here it is for evaluation.
<?xml version="1.0" encoding="UTF-8" ?>
- <fantasy_content xml:lang="en-US" yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/game/223/players" xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" time="5489.1560077667ms" copyright="Data provided by Yahoo! and STATS, LLC" refresh_rate="60" xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng">
- <game>
<game_key>223</game_key>
<game_id>223</game_id>
<name>Football PLUS</name>
<code>pnfl</code>
<type>full</type>
<url>http://football.fantasysports.yahoo.com/f2</url>
<season>2009</season>
- <players count="25">
- <player>
<player_key>223.p.8261</player_key>
<player_id>8261</player_id>
- <name>
<full>Adrian Peterson</full>
<first>Adrian</first>
<last>Peterson</last>
<ascii_first>Adrian</ascii_first>
<ascii_last>Peterson</ascii_last>
</name>
<editorial_player_key>nfl.p.8261</editorial_player_key>
<editorial_team_key>nfl.t.16</editorial_team_key>
<editorial_team_full_name>Minnesota Vikings</editorial_team_full_name>
<editorial_team_abbr>Min</editorial_team_abbr>
- <bye_weeks>
<week>9</week>
</bye_weeks>
<uniform_number>28</uniform_number>
<display_position>RB</display_position>
- <headshot>
<url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</url>
<size>small</size>
</headshot>
<image_url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</image_url>
<is_undroppable>1</is_undroppable>
<position_type>O</position_type>
- <eligible_positions>
<position>RB</position>
</eligible_positions>
<has_player_notes>1</has_player_notes>
</player>
- <player>
</players>
</game>
</fantasy_content>
The two methods I have tried are these (PLEASE NOTE: "xmlContent" is the string that contains the XML listed above):
1.)
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants("name")
select new
{
ID = menu.Element("name").Value,
};
and
2.)
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
foreach (XmlNode row in doc).SelectNodes("//fantasy_content"))
{}
Basically, I get no results enumerated. I have a feeling I am missing some key steps here though. Any help is greatly appreciated. Thank you all.
UPDATE:
As per the awesome suggestions I received, I tried three more things. Since it is not working still, I did not listen very well. :) Actually, that is semi-accurate, please bear with my newbie attempt at working with XML here as I really am thankful for the responses. Here is how I am screwing up the great suggestions, can you offer another tip on what I missed? Thank you all again.
As per Jon Skeet's suggestion (this yields no results for me):
1.) XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants(ns + "fantasy_content")
select new
{
ID = menu.Element("name").Value,
};
As per the second suggestion (this throws me an error):
2.) var result = XElement.Load(xmlContent).Descendants().Where(x => x.Name.LocalName == "name");
As per the combinations of suggesting I need to identify the namespace and Yahoo! guide at: http://developer.yahoo.com/dotnet/howto-xml_cs.html
3.) xmlContent = oauth.AcquirePublicData(rtUrl, "GET");
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("fantasy_content", "http://fantasysports.yahooapis.com/fantasy/v2/base.rng");
XmlNodeList nodes = doc.SelectNodes("/name", ns);
foreach (XmlNode node in nodes)
{
}
This is what's tripping you up:
xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng"
You're asking for elements in the unnamed namespace - but the elements default to the namespace shown above.
It's easy to fix that in LINQ to XML (and feasible but less simple in XmlDocument)
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
...
Note that in your original method you're actually looking for name elements within the name elements - that's not going to work, but the namespace part is probably enough to get you going.
EDIT: It's not clear why you're using an anonymous type at all, but if you really want all the name element values from the document as ID properties in anonymous type instances, just use:
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
.Select(x => new { ID = x.Value });
Note that there's no need to use Descendants(ns + "fantasy_content") as that's just selecting the root element.
Element name consists of two parts: xmlns(namespace) and localname. If xmlns is absent, name is equal to local name. So, you have to create name with namespace or ignore it
You can ignore namespace in your LINQ, just use LocalName
var result = XElement.Load(#"C:\fantasy_content.xml")
.Descendants()
.Where(x => x.Name.LocalName == "name")
.ToList();
I am simply trying to read a particular node from an XML and use it as a string variable in a condition. This gets me to the XML file and gives me the whole thing.
string url = #"http://agent.mtconnect.org/current";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(url);
richTextBox1.Text = xmlDoc.InnerXml;
But I need the power state "ON" of "OFF" (XML section below, can view the whole XML online)
<Events><PowerState dataItemId="p2" timestamp="2013-03-11T12:27:30.275747" name="power" sequence="4042868976">ON</PowerState></Events>
I have tried everything I know of. I am just not that familiar with XML files. and the other posts get me nowhere.
HELP PLEASE!
You may try LINQ2XML for that:
string value = (string) (XElement.Load("http://agent.mtconnect.org/current")
.Descendants().FirstOrDefault(d => d.Name.LocalName == "PowerState"))
If you wanted to avoid LINQ, or if it is not working for you you can use straight XML traversal for this:
string url = #"http://agent.mtconnect.org/current";
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.Load(url);
System.Xml.XmlNamespaceManager theNameManager = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable);
theNameManager.AddNamespace("mtS", "urn:mtconnect.org:MTConnectStreams:1.2");
theNameManager.AddNamespace("m", "urn:mtconnect.org:MTConnectStreams:1.2");
theNameManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
System.Xml.XmlElement DeviceStreams = (System.Xml.XmlElement)xmlDoc.SelectSingleNode("descendant::mtS:DeviceStream", theNameManager);
System.Xml.XmlNodeList theStreams = DeviceStreams.SelectNodes("descendant::mtS:ComponentStream", theNameManager);
foreach (System.Xml.XmlNode CompStream in theStreams)
{
if (CompStream.Attributes["component"].Value == "Electric")
{
System.Xml.XmlElement EventElement = (System.Xml.XmlElement)CompStream.SelectSingleNode("descendant::mtS:Events", theNameManager);
System.Xml.XmlElement PowerElement = (System.Xml.XmlElement)EventElement.SelectSingleNode("descendant::mtS:PowerState", theNameManager);
Console.Out.WriteLine(PowerElement.InnerText);
Console.In.Read();
}
}
When traversing any document with a default namespace in the root node, I have found it is imperative to have a namespace manager. Without it the document is just un-navigable.
I created this code in a console application. It worked for me. Also I am no guru and I may be making some mistakes here. I am not sure if there is some way to have the default namespace referenced without naming it (mtS). Anyone who knows how to make this cleaner or more efficient please comment.
EDIT:
For one less level of 'clunk' you can change this:
if (CompStream.Attributes["component"].Value == "Electric")
{
Console.Out.WriteLine(((System.Xml.XmlElement)CompStream.SelectSingleNode("descendant::mtS:Events", theNameManager)).InnerText;);
Console.In.Read();
}
because there is only one element in there and its innerText is all you will get.
I have a string :
responsestring = "<?xml version="1.0" encoding="utf-8"?>
<upload><image><name></name><hash>SOmetext</hash>"
How can i get the value between
<hash> and </hash>
?
My attempts :
responseString.Substring(responseString.LastIndexOf("<hash>") + 6, 8); // this sort of works , but won't work in every situation.
also tried messing around with xmlreader , but couldn't find the solution.
ty
Try
XDocument doc = XDocument.Parse(str);
var a = from hash in doc.Descendants("hash")
select hash.Value;
you will need System.Core and System.Xml.Linq assembly references
Others have suggested LINQ to XML solutions, which is what I'd use as well, if possible.
If you're stuck with .NET 2.0, use XmlDocument or even XmlReader.
But don't try to manipulate the raw string yourself using Substring and IndexOf. Use an XML API of some description. Otherwise you will get it wrong. It's a matter of using the right tool for the job. Parsing XML properly is a significant chunk of work - work that's already been done.
Now, just to make this a full answer, here's a short but complete program using your sample data:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string response = #"<?xml version='1.0' encoding='utf-8'?>
<upload><image><name></name><hash>Some text</hash></image></upload>";
XDocument doc = XDocument.Parse(response);
foreach (XElement hashElement in doc.Descendants("hash"))
{
string hashValue = (string) hashElement;
Console.WriteLine(hashValue);
}
}
}
Obviously that will loop over all the hash elements. If you only want one, you could use doc.Descendants("hash").Single() or doc.Descendants("hash").First() depending on your requirements.
Note that both the conversion I've used here and the Value property will return the concatenation of all text nodes within the element. Hopefully that's okay for you - or you could get just the first text node which is a direct child if necessary.
var val = XElement.Parse();
val.Descendants(...).Value
Get your xml well formed and escape the double quotes with backslash. Then apply the following code
XDocument resp = XDocument.Parse("<hash>SOmetext</hash>");
var r= from element in resp.Elements()
where element.Name == "hash"
select element;
foreach (var item in r)
{
Console.WriteLine(item.Value);
}
You can use an xmlreader and/or xpath queries to get all desired data.
XmlReader_Object.ReadToFollowing("hash");
string value = XmlReader_Object.ReadInnerXml();
simple question but I've been dinking around with it for an hour and it's really starting to frustrate me. I have XML that looks like this:
<TimelineInfo>
<PreTrialEd>Not Started</PreTrialEd>
<Ambassador>Problem</Ambassador>
<PsychEval>Completed</PsychEval>
</TimelineInfo>
And all I want to do is use C# to get the string stored between <Ambassador> and </Ambassador>.
So far I have:
XmlDocument doc = new XmlDocument();
doc.Load("C:\\test.xml");
XmlNode x = doc.SelectSingleNode("/TimelineInfo/Ambassador");
which selects the note just fine, now how in the world do I get the content in there?
May I suggest having a look at LINQ-to-XML (System.Xml.Linq)?
var doc = XDocument.Load("C:\\test.xml");
string result = (string)doc.Root.Element("Ambassador");
LINQ-to-XML is much more friendly than the Xml* classes (System.Xml).
Otherwise you should be able to get the value of the element by retrieving the InnerText property.
string result = x.InnerText;
The InnerText property should work fine for you.
http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.innertext.aspx
FWIW, you might consider switching API to linq-to-xml (XElement and friends) as IMHO it's a friendly, easier API to interact with.
System.Xml version (NOTE: no casting to XmlElement needed)
var xml = #"<TimelineInfo>
<PreTrialEd>Not Started</PreTrialEd>
<Ambassador>Problem</Ambassador>
<PsychEval>Completed</PsychEval>
</TimelineInfo>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var node = doc.SelectSingleNode("/TimelineInfo/Ambassador");
Console.WriteLine(node.InnerText);
linq-to-xml version:
var xml = #"<TimelineInfo>
<PreTrialEd>Not Started</PreTrialEd>
<Ambassador>Problem</Ambassador>
<PsychEval>Completed</PsychEval>
</TimelineInfo>";
var root = XElement.Parse(xml);
string ambassador = (string)root.Element("Ambassador");
Console.WriteLine(ambassador);
XmlDocument doc = new XmlDocument();
doc.Load("C:\\test.xml");
XmlNode x = doc.SelectSingleNode("/TimelineInfo/Ambassador");
x.InnerText will return the contents
Try using Linq to XML - it provides a very easy way to query xml datasources - http://msdn.microsoft.com/en-us/library/bb387098%28v=VS.100%29.aspx