c# adding xml element where attribute equals - c#

I have an xml file that has a bunch of channels, and I want to append a channel category to every single one of them. Depending on what channel it is. I'm very new to this so please excuse me if this is an obvious error.
example:
<channel-category>Entertainment</channel-category>
or
<channel-category>News</channel-category>
I have tried the following:
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
if (exists("channelname1"))
{
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNode root = doc.DocumentElement;
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
XmlNode category = doc.CreateElement("channel-category");
category.InnerText = "channelcataegorygoeshere";
node.AppendChild(category);
doc.DocumentElement.AppendChild(node);
}
else
{
Console.WriteLine("not found");
}
Console.ReadKey();
}
static bool exists(string channelname)
{
string path;
string xmlfile = "/channels.xml";
path = Environment.CurrentDirectory + xmlfile;
XDocument xmlDoc = XDocument.Load(path);
bool doesexists = (from data in xmlDoc.Element("list").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
return doesexists;
}
but it's giving me the following error and I can't figure it out.. What am I doing wrong?
An unhandled exception of type 'System.Xml.XPath.XPathException' occurred in System.Xml.dll
Additional information: 'list/channel[#id='channelname1'' has an invalid token.
from this line
XmlNode node = root.SelectSingleNode("list/channel[#id='channelname1'");
My XML looks like this
<?xml version="1.0" encoding="UTF-8"?>
<list info="list">
<channel id="channelname1">
<display-name lang="en">channelname1</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname2">
<display-name lang="en">channelname2</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname3">
<display-name lang="en">channelname3</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
<channel id="channelname4">
<display-name lang="en">channelname4</display-name>
<icon src="http://locationtologo.com/" />
<url>http://someurl.com</url>
</channel>
</list>

You dont have closing bracket in list/channel[#id='channelname1'(HERE).
Moreover, you are actually trying to do following:
var doc = new XmlDocument();
doc.Load(Environment.CurrentDirectory + "\\channels.xml");
var nodes = doc.SelectNodes("list/channel[#id=\"channelname1\"]");
if (nodes != null)
{
foreach (XmlNode node in nodes)
{
var el = doc.CreateElement("channel-category");
el.InnerText = "SomeValue";
node.AppendChild(el);
}
}

bool doesexists = (from data in xmlDoc.Element("tv").Elements("channel")
where (string)data.Attribute("id") == channelname
select data).Any();
You are trying to reach the channel node where the id equals the channelname inside tv. The problem is that tv does not exist, the channels are inside this:
<list info="list">
Solution: either put the channels into tv, or use a selector appropriate to your current structure.

Why you are using tv instead of list thats why xml library not getting your path of your elements and throwing this error.
try this..
XmlNode node = root.SelectSingleNode("list/channel");
node.Attributes["id"].Value=="channelname1"?true:false;

Related

Select single node

From the following xml:
<response>
<content>
<Result xmlns="http://www.test.com/nav/webservices/types">
<Name>Test</Name>
</Result>
</content>
<status>ok</status>
</response>
I am trying to get the value of the Name element the following way but that does not work:
private static void Main()
{
var response = new XmlDocument();
response.Load("Response.xml");
var namespaceManager = new XmlNamespaceManager(response.NameTable);
namespaceManager.AddNamespace("ns", "http://www.test.com/nav/webservices/types");
Console.WriteLine(response.SelectSingleNode("/response/content/Result/Name", namespaceManager).InnerXml);
}
How can I select the Name element?
Your code would have worked just fineif the Xml had defined the namespace with a "ns:" prefix.
But in this case, the namespace is given without any prefix, which sets the default namespace for everything in the Result tag to ".../webservice/types".
To reflect this, you need to modify the Xpath, and tell the XmlDocument that the nodes you are looking for under Resultare in the webservice/types namespace. So your query will look like this:
Console.WriteLine(response.SelectSingleNode(#"/response/content/ns:Result/ns:Name", namespaceManager).InnerXml);
For getting directly the text value of a node there is a text() function, if used in the query it would look like:
/response/content/Result/Name/text()
Try this:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.InnerXml = "<response><content><Result xmlns=\"http://www.test.com/nav/webservices/types\"><Name>Test</Name></Result></content><status>ok</status>";
string elementValue = String.Empty;
if (xmlDoc != null)
{
xNode = xmlDoc.SelectSingleNode("/Result");
xNodeList = xNode.ChildNodes;
foreach (XmlNode node in xNodeList)
{
elementValue = node.InnerText;
}
}

Reading xml file on some condition

I want to use a xml file which is as below
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page name="Default.aspx">
<method name="Login_click">
<message code="0" description="this is a test description">
<client code="0000000000" description="this is a description for clent 0000000000" />
</message>
</method>
</page>
</pages>
Now I want to create a function like below
public static string GetAppMessage(string pageName, string methodName, string clientCode, string code)
{
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
var doc = new XmlDocument();
doc.Load(xmlFile);
if (string.IsNullOrEmpty(clientCode))
{
//go to node who page name to pageName
//and read the vlue
}
else
{
//read for a particular client code
}
}
How can I do this.
Edit 1
Do I need to loop through each node or can I reach to a particular node directly and find the decedent nodes.
like below
foreach (XmlNode chldNode in doc.ChildNodes)
{
....
Instead of XmlDocument you can use XDocument and LINQ to xml:
var xmlFile = HttpContext.Current.Server.MapPath("~/App_Data/Theme.xml");
XDocument xmlDoc = XDocument.Load(xmlFile);
var xmlPage = (from page in xmlDoc.Descendants()
where page.Name.LocalName == "page"
&& page.Attribute("name").Value == pageName
select page).FirstOrDefault();
if (xmlPage != null)
{
//do what you need
}
When you use XmlDocument and you know how the XML-file will look like (I mean you know the names of the nodes where the information is inside) then you could do something like this:
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement root = doc["NameOfRootNode"];
if (root != null)
{
//For nodes you just need to bypass to get to another subnode:
XmlNode node = root.SelectSingleNode("nameOfAnotherNode");
//For nodes you actually want to do something with, like read text, attribute etc.
if (node != null && node.SelectSingleNode("nameOfOneMoreNode") != null)
{
var xmlElement = node["nameOfOneMoreNode"];
if (xmlElement != null)
{
//Read from the xmlElement you selected and do something with it...
}
}
//...
}
With SelectSingleNode or SelectNodes you can maneuver to a specific known node and can read the InnerText or an Attribute.
You can use XPath to get <page> element by it's name attribute, for example :
string xpath = "//page[#name='{0}']";
XmlNode page = doc.SelectSingleNode(string.Format(xpath, pageName));
//and read the vlue
Basically, above XPath look for <page> element having name attribute equals pageName parameter.

Reading xml with XmlReader

Here is xml that I want to read
<servers>
<server name=" PIOU PIOU || OPTIMAL" ca="1" continent_code="EU"
country="France" country_code="FR" ip="s1.mymumble.fr"
port="20129" region="" url="http://www.mymumble.fr" />
</servers>
Now when I read it I successfully get the elemnt 'servers' and I can read its innerxml as well i.e the correct node is created. but when I create a node with element 'server' the node is empty.
I guess the reason is the short form of starting and ending element used in the 'server' node.
But my problem is that the xml comes from some remote server and I cannot modify it, so I have to read it the way it is written.
Here is my code to read XML:
List<XmlNode> nodeList = new List<XmlNode>();
XmlDocument doc = new XmlDocument();
while (reader.Read())
{
//keep reading until we see my element
if (reader.Name.Equals("server") && (reader.NodeType == XmlNodeType.Element))
{
XmlNode myNode = doc.ReadNode(reader);
Debug.Log(reader.IsEmptyElement ? "its empty" : "not empty");
//this always prints "its empty"
nodeList.Add(myNode);
}
}
foreach( XmlNode node in nodeList)
{
Debug.Log("child:\t"+node.FirstChild.InnerXml);
}
See the documentation for IsEmptyElement. In particular, this example:
<item num="123"/> (IsEmptyElement is true).
<item num="123"></item> (IsEmptyElement is false, although element content is empty).
Your node is considered empty because it uses the short form. There is no element "content," but there are attributes.
Have you checked the node that you created (i.e. myNode) to see that it contains the attributes?
XmlReader is kind of low level for your needs...
Wouldn't it be easier to parse your XML with Linq2Xml?
var xmlStr = #"<servers>
<server name="" PIOU PIOU || OPTIMAL"" ca=""1"" continent_code=""EU""
country=""France"" country_code=""FR"" ip=""s1.mymumble.fr""
port=""20129"" region="""" url=""http://www.mymumble.fr"" />
</servers>";
var doc = XDocument.Parse(xmlStr);
var servers = doc.Root.Elements("server")
.Select(e => new{
name = (string)e.Attribute("name"),
ca = (int)e.Attribute("ca"),
country = (string)e.Attribute("country")
//etc...
});
XmlDocument has method called "GetNodes", that will help you.

parsing attribute from xml file

I'm trying to parse the following:
<?xml version="1.0" encoding="utf-8"?>
<GC>
<CREATED>01/23/2014 16:10:18</CREATED>
<DATA>
<CONTAINER name="home" type="xml" version="1.1.0.0">
<HEADER>
<ATTRIBUTE name="lang" value="EN" />
<ATTRIBUTE name="destination" value="UK" />
</HEADER>
</CONTAINER>
</DATA>
</GC>
How do I go about finding the value when name="lang"?
So far I have this:
XmlDocument Doc = new XmlDocument();
Doc.Load(#path);
XmlNode node = Doc.DocumentElement.SelectSingleNode("/GC/DATA/CONTAINER/HEADER/ATTRIBUTE/NAME");
string SI = node.Attributes["lang"].InnerText;
Doesn't seem to work unfortunately, could use some help. Many thanks.
This will do it:
XmlNode node =
Doc.SelectSingleNode("/GC/DATA/CONTAINER/HEADER/ATTRIBUTE[#name = 'lang']/#value");
string SI = node.InnerText;
And I would advise using a null check:
XmlNode node =
Doc.SelectSingleNode("/GC/DATA/CONTAINER/HEADER/ATTRIBUTE[#name = 'lang']/#value");
string SI = null;
if(node != null)
{
SI = node.InnerText;
}
With using LINQ to XML you can get it like this:
XDocument xDoc = XDocument.Load("path");
var element = xDoc.Descendans("ATTRIBUTE").First();
var nameAttribute = (string)element.Attribute("name");
This will get you the value of the attribute in the ATTRIBUTE tag which has name == lang:
XmlDocument Doc = new XmlDocument();
Doc.Load(#path);
XmlNode node = Doc.DocumentElement.SelectSingleNode("/GC/DATA/CONTAINER/HEADER/ATTRIBUTE[#name='lang']");
string SI = node.Attributes["value"].InnerText;
XmlNode node = Doc.DocumentElement.SelectSingleNode("/GC/DATA/CONTAINER/HEADER/ATTRIBUTE[#name='lang']");
string SI = node.Attributes["value"].Value;

How to parse nested XML in C#

I'm working with an API and retrieving the data in XML. Here's my XML:
<RTT>
<AgencyList>
<Agency Name="Caltrain" HasDirection="True" Mode="Rail">
<RouteList>
<Route Name="BABY BULLET" Code="BABY BULLET">
<RouteDirectionList>
<RouteDirection Code="SB2" Name="SOUTHBOUND TO TAMIEN">
<StopList>
<Stop name="Sunnyvale Caltrain Station" StopCode="70222">
<DepartureTimeList/>
</Stop>
</StopList>
</RouteDirection>
<RouteDirection Code="NB" Name="NORTHBOUND TO SAN FRANCISCO">
<StopList>
<Stop name="Sunnyvale Caltrain Station" StopCode="70221">
<DepartureTimeList>
<DepartureTime>69</DepartureTime>
</DepartureTimeList>
</Stop>
</StopList>
</RouteDirection>
</RouteDirectionList>
</Route>
<Route Name="LIMITED" Code="LIMITED">...</Route>
<Route Name="LOCAL" Code="LOCAL">...</Route>
</RouteList>
</Agency>
</AgencyList>
</RTT>
Not every DepartureTimeList will have a DepartureTime child node. Here's what I got so far, which retrieves the Route name:
List<string> trainType = new List<string>();
XDocument doc = XDocument.Load("http://services.my511.org/Transit2.0/GetNextDeparturesByStopName.aspx?token=0f01ac4a-bc16-46a5-8527-5abc79fee435&agencyName=Caltrain&stopName=" + DropDownList1.SelectedItem.Text.ToString());
doc.Save("times.xml");
string feed = doc.ToString();
XmlReader r = XmlReader.Create(new StringReader(feed));
r.ReadToFollowing("RouteList");
if (r.ReadToDescendant("Route"))
{
do
{
trainType.Add(r.GetAttribute("Name"));
} while (r.ReadToNextSibling("Route"));
}
I'm mostly interested in the departure time (if it exists) and I've been struggling all afternoon to try and parse it.
Try this... Hopefully this will do it.
XmlDocument doc = new XmlDocument();
doc.Load("xml path");
XmlNode node = doc.SelectSingleNode("/RTT");
foreach (XmlNode nodes in node.SelectNodes(
"/AgencyList/Agency Name/RouteList/Route"))
{
trainType.Add(r.GetAttribute("Name"));
XmlNode s = nodes.SelectSingleNode("Route Name/RouteDirectionList/RouteDirection Code/StopList/Stop");
if (s != null && s["DepartureTimeList"].HasChildNodes)
{
// do stuff here
}
}

Categories

Resources