I know you two ways, but don't work as I want:
1. [INode].ToString();
This returns the value in my node plus a "^^[predicate uri]", like this;
random node value.^^http://www.w3.org/2001/XMLSchema#string
2. [INode].ReadXml(Xml reader); I don't know how to use, coz I can't find any examples.
Is there a way of retrieving only the value of the node?
Or is the "XmlRead()" methode what I need? How do I use it?
Based on the NodeType you can cast to the appropriate interface and then access the value e.g.
switch (node.NodeType)
{
case NodeType.Literal:
return ((ILiteralNode)node).Value;
case NodeType.Uri:
return ((IUriNode)node).Uri.ToString();
// etc.
}
Or you might want to use node.AsValuedNode().AsString() if you are sure that your node is a literal
Note that the ReadXml()/WriteXml() methods are for .Net XML serialisation and are not intended for general use.
To get content you should use WriteXml instead of ReadXml function
var sb = new StringBuilder();
var xmlWriterSettings = new XmlWriterSettings
{ // It's required in my case but maybe not in your try different settings
ConformanceLevel = ConformanceLevel.Auto
};
using (var writer = XmlWriter.Create(sb, xmlWriterSettings))
rdfType.WriteXml(writer);
var result = sb.ToString();
I seem to have misundestood the XmlReader and XmlWriter, also understand the way of use, but don't seem to get it working.
This message I get:
InvalidOperationException: This XmlWriter does not accept Attribute at this state Content.
I suppose I do need to tweak the XmlWritterSettings in order to make it work.
I don't see any documantion on the required XmlWritterSettings for reading the DotNetRDF INodes, so I shall use the "ToString()" for now.
this is the part of the RDF/XML that the node holds:
<property:Paragraph rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Geef houvast.</property:Paragraph>
Isn't there some other way to extract the "Geef houvast." between the element?
Thanks for helping me understand the XmlReader/XmlWriter now!
Related
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
I have an XML document that resembles this:
<resorts>
<resort location="locationA" email="locationA#somewhere.com"></resort>
<resort location="locationB" email="locationB#somewhere.com"></resort>
<resort location="locationC" email="locationC#somewhere.com"></resort>
<resort location="locationD" email="locationD#somewhere.com"></resort>
</resorts>
I need to get the corresponding email address given a specific location and the code I'm using to do that is:
XmlDocument doc = new XmlDocument();
doc.Load(xml);
XmlElement xmlRoot = doc.DocumentElement;
XmlNodeList xmlNodes = xmlRoot.SelectNodes("/resorts/resort");
foreach(XmlNode element in xmlNodes)
{
foreach (XmlAttribute attribute in element.Attributes)
{
switch (attribute.Name)
{
case "location":
if (attribute.Value.ToLower() == location.ToLower())
{
loc = attribute.Value;
locationIdentified = true;
}
break;
case "email":
if (locationIdentified)
{
if(!emailIdentified)
{
email = attribute.Value;
var recipientList = new Dictionary<string, string>() { { "emailrecipients", email } };
emailRecipients.Add(recipientList);
emailIdentified = true;
}
}
break;
}
}
}
return recipients;
But I don't really care much for the iterative approach and would prefer something more streamlined with less code.
Something similar to a linq expression would be ideal but I don't typically have to deal much with XML data so I'm a bit of a novice in this area; but I know there has to be a better way to get retrieve the data from the XML.
What I need to do is acquire the email address for a specific location; having the location known beforehand.
What would be the most efficient manner to do this without an explicit iteration as I've done here?
This question is not specifically about "how to use" alternative options as much as "what are more streamlined approaches" to solve this problem. However, as I stated; since I am a novice at XML, it would be nice to have examples of any proposed alternatives
Thanks
octavioccl's answer is correct, but if you have some reason to stick with XmlDocument rather than switching to Linq to XML (perhaps this is a large project that's already heavily committed to the old XML classes, and nobody wants to cross the streams), this will work:
string location = "locationC";
string xpath = "/resorts/resort[#location='" + location + "']/#email";
var address = doc.SelectNodes(xpath).Cast<XmlAttribute>()
.Select(attr => attr.Value).SingleOrDefault();
If you can use C#6 features, this is tidier:
var address = doc.SelectSingleNode(xpath)?.Value;
If you need it to be case-blind, I think you may be stuck with this:
string xpath = "/resorts/resort[translate(#location, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='"
+ location.ToLower() + "']/#email";
There's a lower-case() function in XPath 2.0, but SelectNodes seems to be implementing XPath 1.0. That messy translate() call is the usual workaround.
If you use Linq to XML your query could be like this:
var query=root.Descendants("resort").Where(e=>e.Attribute("location").Value.ToLower()==location.ToLower())
.Select(e=>e.Attribute("email"));
If there is only one with that location you can use FirstOrDefault extension method to get it:
var result=query.FirstOrDefault();
Its hard to say what is most efficient, but you could look into using XPath to traverse XML without having to iterate.
Here are some examples of what that looks like:
https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx
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);
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();
I've currently got an XML element in my database that maps to an object (Long story short the XML is complicated and dynamic enough to defy a conventional relational data structure without massive performance hits).
Around it I've wrapped a series of C# objects that encapsulate the structure of the XML. They work off a base class and there's multiple different possible classes it'll deserialize to with different data structures and different implemented methods. I'm currently wrapping the functionality to serialize/deserialize these into a partial class of the LINQ-generated database objects.
My current approach to this is:
public Options GetOptions()
{
if (XmlOptions == null) return null;
XmlSerializer xs = new XmlSerializer(typeof(Options));
return (Options)xs.Deserialize(XmlOptions.CreateReader());
}
public void SetOptions(Options options)
{
if (XmlOptions == null) Options = null;
else
{
XmlSerializer xs = new XmlSerializer(typeof(Options));
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, options);
XmlOptions = System.Xml.Linq.XElement.Parse(System.Text.UTF8Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
(To help with reading given the changed names aren't too clear, XmlOptions is the XElement element from LINQ and Options is my class it deserializes into)
Now, it works. But that's not really enough to call it "finished" to me :P It just seems incredibly inefficient to serialize XML to a memory stream, convert it to a string, then re-parse it as XML. My question is - Is this the cleanest way to do this? Is there a more efficient/tidy mechanism for doing the serialization? Is there a better approach that'll give me the same benefits (I've only got test data in the system so far, so I can change the XML structure if required)?
PS: I've renamed the fields to be more generic - so don't really need comments about naming conventions ;)
XmlSerializer.Serialize has an overload that takes an XmlWriter.
You can create an XmlWriter that writes to an existing XElement by calling the CreateWriter method.
You can therefore write the following:
static readonly XmlSerializer xs = new XmlSerializer(typeof(Options));
...
var temp = new XElement("Parent");
using (var writer = temp.CreateWriter())
xs.Serialize(writer, options);
XmlOptions = Temp.FirstNode;