Get a string from xml files - c#

i have a coode that are trying to get the id of xml file where url exist.
My code
public static string GetPageIdByUrl(string url)
{
string folder = HttpContext.Current.Server.MapPath("/App_Data/Pages/" + Path.DirectorySeparatorChar);
foreach (string file in Directory.GetFiles(folder, "*.xml", SearchOption.TopDirectoryOnly))
{
var xml = XDocument.Load(file);
var q = from f in xml.Descendants("Page")
where (string)f.Element("Url").Value == url
select (string)f.Attribute("Id").Value;
if (q.Count() != 0)
{
return q.SingleOrDefault();
}
}
return null;
}
I get this error:
Object reference not set to an instance of an object.
Line: select f.Attribute("Id").Value;
if "test" exist in a xml file <Url>test</Url> i want it to return the attribute ID. But that sems only work for the first xml file.
Hope you get the problem.

Try posting some sample XML.
I'm guessing your Url element doesn't have an "Id" (exact spelling) attribute. Therefore the XNode is null and cannot provide a Value.

Is Id an attribute on the Url element?
<Pages>
<Page><Url Id="6428">http://www.example.com/</Url></Page>
</Pages>
Then you need something like this:
var q = from f in xml.Descendants("Page")
where (string)f.Element("Url") == url
select (string)f.Element("Url").Attribute("Id");
(Note that you can leave out .Value when using casts as XElement and XAttribute define casts to various base types. This is often a source of unexpected NullReferenceExceptions.)

I mabye tirred today,the problem was that in the xml file the attribute was calld "id" and not "Id".

Have you considered using XPath instead?
It's been a while since I've used the syntax, but the query should look something similar to this:
/Page[Url='THE_STRING']
Using SelectNodes(), you should get a list of nodes that fit to your query, and from whom you could get the attribute's value.

Related

Cannot access or find reference to System.Xml.Linq.LineInfoAnnotation. Why is this?

I have an application which takes an XML document and sorts it by certain attributes. I have information associated with each line of the XML document which I want to include in the sorted document. In order to do this,
When I load the file, I make sure the line info is loaded using XDocument.Load(file, LoadOptions.SetLineInfo).
Then I recursively iterate over each XElement and get its line info. When I ran the app, I noticed that each XElement has two annotations,
one of type System.Xml.Linq.LineInfoAnnotation
and one of type System.Xml.Linq.LineInfoEndElementAnnotation.
They contain the info that I need but in private fields.
I can't find any information on these classes, I can't instantiate them, they do not appear in the Object browser under System.Xml.Linq. Yet they exist and I can run "GetType()" on them and get information about the class.
If they exist, why are they not in MSDN references and why can't I instantiate them or extend them? Why can't I find them in the object browser?
P.S. My workaround for this was to use reflection to get the information contained inside each element. But I still can't pass a class name to tell the method what type it is, I have to isolate the object from XElement.Annotations(typeof(object)), and then run GetType() on it. I've illustrated this below.
public object GetInstanceField(Type type, object instance, string fieldName)
{
//reflective method that gets value of private field
}
XElement xEl = existingXElement; //existingXElement is passed in
var annotations = xEl.Annotations(typeof(object)); //contains two objects, start and end LineInfoAnnotation
var start = annotations.First();
var end = annotations.Last();
var startLineNumber = GetInstanceField(start.GetType(), start, lineNumber); //lineNumber is private field I'm trying to access.
var endLineNumber = GetInstanceField(end.GetType(), end, lineNumber);
This code works, but again, I can't just tell the method "typeof(LineInfoAnnotation)", instead I have to do GetType on the existing object. I cannot make sense of this.
Those classes are private - an implementation detail, if you will.
All XObjects (elements, attributes) implement the IXmlLineInfo interface - but they implement the inteface explicitly, so you must perform a cast to access the properties.
Once you have your IXmlLineInfo, you can use the properties LineNumber and LinePosition.
var data =
#"<example>
<someElement
someAttribute=""val"">
</someElement></example>
";
var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(data)), LoadOptions.SetLineInfo);
foreach(var element in doc.Descendants()) {
var elLineInfo = element as IXmlLineInfo;
Console.Out.WriteLine(
$"Element '{element.Name}' at {elLineInfo.LineNumber}:{elLineInfo.LinePosition}");
foreach(var attr in element.Attributes()) {
var attrLineInfo = attr as IXmlLineInfo;
Console.Out.WriteLine(
$"Attribute '{attr.Name}' at {attrLineInfo.LineNumber}:{attrLineInfo.LinePosition}");
}
}
Output:
Element 'example' at 1:2
Element 'someElement' at 2:2
Attribute 'someAttribute' at 3:3
To get the EndElement information, you have to use a plain old XML reader, since the XObject api doesn't expose any information about where the element ends.
using(var reader = doc.CreateReader()) {
while(reader.Read()) {
var lineInfo = reader as IXmlLineInfo;
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
if(reader.NodeType == XmlNodeType.Element && reader.HasAttributes) {
while(reader.MoveToNextAttribute()) {
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
}
}
}
}
Output:
Element example at 1:2
Element someElement at 2:2
Attribute someAttribute at 3:3
EndElement someElement at 5:5
EndElement example at 5:19

XML LINQ query returns nothing

I'm trying to parse an xml file using LINQ, but as I understand the query returns null. (It's WP7)
Here's the code:
var resultQuery = from q in XElement.Parse(result).Elements("Question")
select new Question
{
QuestionId = q.Attribute("id").Value,
Type = q.Element("Question").Attribute("type").Value,
Subject = q.Element("Subject").Value,
Content = q.Element("Content").Value,
Date = q.Element("Date").Value,
Timestamp = q.Element("Timestamp").Value,
Link = q.Element("Link").Value,
CategoryId = q.Element("Category").Attribute("id").Value,
UserId = q.Element("UserId").Value,
UserNick = q.Element("UserNick").Value,
UserPhotoURL = q.Element("UserPhotoURL").Value,
NumAnswers = q.Element("NumAnswers").Value,
NumComments = q.Element("NumComments").Value,
};
"result" is the xml string, just like this one.
http://i48.tinypic.com/1ex5s.jpg (couldn't post properly formatted text so here's a pic : P )
Error:
http://i48.tinypic.com/2uyk2ok.jpg
Sorry, if I haven't explained it properly and if this has already been asked (tried searching but didn't help).
You have run into an XML namespace problem. When you are just querying "Question", the string is translated into an XName with the default namespace. There are no elements in the default namespace in your XML, only elements in the urn:yahoo:answers namespace (see the top level element, where it says xmlns="urn:yahoo:answers").
You need to query the correct XML namespace, like this:
var ns = new XNameSpace("urn:yahoo:answers");
var resultQuery = from q in XElement.Parse(result).Elements(ns + "Question");
When picking out the individual properties, remember to add the namespace also.
XName is a class that represents an XML name, which might have a namespace defined by XNameSpace. These two classes has an implicit conversion operator implemented that allows you to implicitly convert from string to XName. This is the reason the calls work by just specifying a string name, but only when the elements are in the default namespace.
The implicitness of this makes it very easy easier to work with XML namespaces, but when one does not know the mechanism behind, it gets confusing very quickly. The XNameclass documentation has some excellent examples.
Two ways to fix it:
Add the root element was part since Elements only search one level - XElement.Parse(result).Root.Elements("Question")
Use the Descendants method since that will search the entire xml tree.

XML linq query lists first elements but not all

I have this XML file that I parse into its elements and create a list of a custom object Module.
XDocument kobra = XDocument.Load(new StringReader(results.OuterXml));
XNamespace ns = "#RowsetSchema";
var kobraNodeList = from s in kobra.Descendants(ns + "row")
select new Module
{
id = s.Attribute("ows_ID").Value,
name = s.Attribute("ows_Title").Value,
sourceFile = s.Attribute("ows_Source_x0020_Message_x0020_File_").Value,
scope = Scope.KOBRA,
component = string.Empty
};
and here's my Module struct:
public struct Module
{
public string name;
public Scope scope;
public string component;
public int wordCound;
public string id;
public string sourceFile;
}
The code works fine, but things get weird when I try to convert the var kobraNodeList into a list of Modules, I get a System.NullReferenceException at the AddRange line:
this.moduleList = new List<Module>;
this.moduleList.AddRange(kobraNodeList);
When trying to debug, I notice that although kobraNodeList.Count() also returns System.NullReferenceException, a kobraNodeList.Any() returns true, and kobraNodeList.First() returns a perfectly valid and correct Module struct with the desired data.
The XML file is valid, and if I replace the linq query with this:
var kobraNodeList = from s in kobra.Descendants(ns + "row")
select s;
I get a valid list of XElement, which I can Count() ok.
Can someone explain me what's wrong? BTW, I'm using .NET 3.5.
That looks like one (or more) of kobra.Descendants has ows_ID, ows_Title or ows_Source_x0020_Message_x0020_File_ attribute missing.
Linq uses deferred execution, so it won't try to build the sequence until you ask for the items. When you call Any() or First(), it only needs the first item in the sequence to work, which tells me that the first item in kobra.Descendants does have all of the required nodes.
However, one of the items after the first is probably missing at least one of those attributes - so you end up asking for the Value of a NULL attribute.
Inside
select new Module
{
// properties...
}
You could be running into a NullReferenceException as you access .Value on elements that might not exist in the XML document. Your first object in the collection is likely fine, hence your results when using Any() or First(). Subsequent items could be missing elements/attributes you are trying to use.
Try this as a replacement instead of using .Value directly.
id = (string)s.Attribute("whatever") // etc.
One of your lines such as s.Attribute("ows_Source_x0020_Message_x0020_File_") will be returning null for one of the records so s.Attribute("ows_Source_x0020_Message_x0020_File_").Value would cause the null reference exception.

How to read a key from app setting file when value is know?

I am developing an application in .NET Framework using C#, in my application I have the necessity of getting a value from an XML file. I have written the following code, to get the value when the key is provided by searching for the key in the XML file.
XmlDocument appSettingsDoc = new XmlDocument();
appSettingsDoc.Load(Assembly.GetExecutingAssembly().Location + ".config");
XmlNode node = appSettingsDoc.SelectSingleNode("//appSettings");
XmlElement value = (XmlElement)node.SelectSingleNode(string.Format("//add[#key='{0}']", key));
return (value.GetAttribute("value"));
But I am unable to get the key name when the value is given, for example, if the file contains
`<add key="keyname" value="keyvalue" />`
and if I provide "keyvalue" I want to get "keyname". I know that I am reading from the appconfig file and there is an other way also(i.e using configurationmanager) but I want to read it using XML.
Please help me out.
Thanks,
Bibhu
Does this not work?
XmlDocument appSettingsDoc = new XmlDocument();
appSettingsDoc.Load(Assembly.GetExecutingAssembly().Location + ".config");
XmlNode node = appSettingsDoc.SelectSingleNode("//appSettings");
XmlElement value = (XmlElement)node.SelectSingleNode(string.Format("//add[#value='{0}']", value));
return (value.GetAttribute("key"));
Note that this system assumes that each value in your appSettings is unique, or else you'll only get the first key with the specified value.
If I were implementing this, by the way, I would just construct a new dictionary from the ConfigurationManager.AppSettings dictionary, using values as keys and keys as values. Reading the appSettings section of the config file via the XML interface when it is already parsed for you into a dictionary is definitely a code smell.
try using this method
private static string readConfig(string value)
{
System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Windows.Forms.Application.ExecutablePath);
System.Configuration.AppSettingsSection ass = config.AppSettings;
foreach (System.Configuration.KeyValueConfigurationElement item in ass.Settings)
{
if (item.Value == value)
return item.Key;
}
return null;
}
To find key based on value you can still use the ConfigurationManager class, can't see any reason to replace it with your own code.
So, sample code would be:
string myKey = ConfigurationManager.AppSettings.AllKeys.ToList().FirstOrDefault(key =>
{
return ConfigurationManager.AppSettings[key] == "keyvalue";
});
Instead of
<add key="keyname" value="keyvalue" />
use
<add key="keyvalue" value="keyname" />
Thats how its meant to be.
Do it using XPath query, don't have time to mock one up now.
try to use Linq to XMl like this
XDocument loaded = XDocument.Load(#"XmlFile.xml");
var q = from c in loaded.Descendants("add")
where (String)c.Attribute("value") == "value1"
select c;
foreach(var item in q)
Console.WriteLine(item.Attribute("key").Value);

Convert XElement to string

I have a simple XElement object
XElement xml = new XElement("XML",
new XElement ("TOKEN",Session["Token"]),
new XElement("ALL_INCLUSIVE", "0"),
new XElement("BEACH", "0"),
new XElement("DEST_DEP", ddlDest.SelectedValue.ToString()),
new XElement("FLEX", "0")
);
Where want to dump out the contents into a string. Exactly like how Console.Writeline(xml); does, but I want the contents in a string. I tried various methonds. xml.ToString(); doesn't return anything on its own.
ToString should most definitely work. I use it all the time. What does it return for you in this case? An empty string? My guess is that something went wrong building your XElement. To debug, rewrite the code to add each of the child XElements separately, so that you can step through your code and check on each of them. Then before you execute the .ToString, in the Locals window, look at the [xml] variable expanded to xml.
In short, your problem is happening before you ever get to the ToString() method.
ToString works, but it returns content including XElement tag itself. If you need for Inner XML without root tag ("" in your example), you may use the following extension method:
public static class XElementExtension
{
public static string InnerXML(this XElement el) {
var reader = el.CreateReader();
reader.MoveToContent();
return reader.ReadInnerXml();
}
}
Then simple call it: xml.InnerXML();

Categories