Convert XElement to string - c#

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();

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

How to read value of an INode?

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!

Where is my annotation?

When I try to annotate class, property and method, and then try to retrieve annotated node, only the class one is returned. Why?
Here is the code that annotates
SyntaxAnnotation propertyAnnotation = null;
SyntaxAnnotation classAnnotation = null;
SyntaxAnnotation setMethodAnnotation = null;
document = document
.AnnotateClass(classDeclaration, out classAnnotation)
.AnnotateProperty(propertyDeclaration, out propertyAnnotation)
.AnnotateSetMethod(setMethodDeclaration, out setMethodAnnotation);
I have these extension methods on IDocument defined
internal static IDocument AnnotateSetMethod(this IDocument document, MethodDeclarationSyntax method,
out SyntaxAnnotation annotation)
{
annotation = new SyntaxAnnotation();
var newRoot = document.GetSyntaxRoot()
.ReplaceNode(method, method.WithAdditionalAnnotations(annotation));
return document.UpdateSyntaxRoot(newRoot);
}
internal static IDocument AnnotateProperty(this IDocument document, PropertyDeclarationSyntax property,
out SyntaxAnnotation annotation)
{
annotation = new SyntaxAnnotation();
var newRoot = document.GetSyntaxRoot()
.ReplaceNode(property, property.WithAdditionalAnnotations(annotation));
return document.UpdateSyntaxRoot(newRoot);
}
internal static IDocument AnnotateClass(this IDocument document, ClassDeclarationSyntax classDeclaration,
out
SyntaxAnnotation annotation)
{
annotation = new SyntaxAnnotation();
var newRoot = document.GetSyntaxRoot()
.ReplaceNode(classDeclaration, classDeclaration.WithAdditionalAnnotations(annotation));
return document.UpdateSyntaxRoot(newRoot);
}
public static TSyntaxNode GetAnnotatedNode<TSyntaxNode>(this IDocument document, SyntaxAnnotation annotation)
where TSyntaxNode : CommonSyntaxNode
{
return document.GetSyntaxRoot().GetAnnotatedNode<TSyntaxNode>(annotation);
}
And if I do
var propertyDeclaration = document.GetAnnotatedNode<PropertyDeclarationSyntax>(propertyAnnotation);
I get an error, but if I try with ClassDeclarationSyntax it works fine.
My crystal ball is telling me that the .Replace node calls in all but your AnnotateClass are failing. See if the new roots you get back are the exact same object as the old roots.
This is because once you've added an annotation to the class, you now have a new tree, and so the property syntax you have is no longer "in" that tree -- it's a new node. This is the way of immutability -- once a new node is created somewhere, all the nodes in the tree are effectively new since you can get to any node from any other node. (It's because of this problem that we added syntax annotations in the first place....otherwise you'd make a few nodes and have no way to get back to them.)
You have a few ways to approach this:
Use a SyntaxRewriter where you do the rewrite all in one stage. You override VisitClass, VisitProperty, etc and produce new nodes with annotations all at once.
Rather than calling ReplaceNode, call ReplaceNodes, where you replace all three nodes at once.
In either case, doing a single rewrite is always preferable to a bunch of rewrites for performance reasons. Like I said, once you replace a node in a tree and get back a new root, all your instances have changed and may have to be re-created. This is expensive and produces memory pressure.
[Technically, that statement is a lie: we do build stuff lazily and reuse lots stuff. But the less rewrites the better.]

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.

What is the best way to compare XML files for equality?

I'm using .NET 2.0, and a recent code change has invalidated my previous Assert.AreEqual call (which compared two strings of XML). Only one element of the XML is actually different in the new codebase, so my hope is that a comparison of all the other elements will give me the result I want. The comparison needs to be done programmatically, since it's part of a unit test.
At first, I was considering using a couple instances of XmlDocument. But then I found this:
http://drowningintechnicaldebt.com/blogs/scottroycraft/archive/2007/05/06/comparing-xml-files.aspx
It looks like it might work, but I was interested in Stack Overflow feedback in case there's a better way.
I'd like to avoid adding another dependency for this if at all possible.
Similar questions
Is there an XML asserts for NUnit?
How would you compare two XML Documents?
It really depends on what you want to check as "differences".
Right now, we're using Microsoft XmlDiff: http://msdn.microsoft.com/en-us/library/aa302294.aspx
You might find it's less fragile to parse the XML into an XmlDocument and base your Assert calls on XPath Query. Here are some helper assertion methods that I use frequently. Each one takes a XPathNavigator, which you can obtain by calling CreateNavigator() on the XmlDocument or on any node retrieved from the document. An example of usage would be:
XmlDocument doc = new XmlDocument( "Testdoc.xml" );
XPathNavigator nav = doc.CreateNavigator();
AssertNodeValue( nav, "/root/foo", "foo_val" );
AssertNodeCount( nav, "/root/bar", 6 )
private static void AssertNodeValue(XPathNavigator nav,
string xpath, string expected_val)
{
XPathNavigator node = nav.SelectSingleNode(xpath, nav);
Assert.IsNotNull(node, "Node '{0}' not found", xpath);
Assert.AreEqual( expected_val, node.Value );
}
private static void AssertNodeExists(XPathNavigator nav,
string xpath)
{
XPathNavigator node = nav.SelectSingleNode(xpath, nav);
Assert.IsNotNull(node, "Node '{0}' not found", xpath);
}
private static void AssertNodeDoesNotExist(XPathNavigator nav,
string xpath)
{
XPathNavigator node = nav.SelectSingleNode(xpath, nav);
Assert.IsNull(node, "Node '{0}' found when it should not exist", xpath);
}
private static void AssertNodeCount(XPathNavigator nav, string xpath, int count)
{
XPathNodeIterator nodes = nav.Select( xpath, nav );
Assert.That( nodes.Count, Is.EqualTo( count ) );
}
Doing a simple string compare on a xml string not always work. Why ?
for example both :
<MyElement></MyElmennt> and <MyElment/> are equal from an xml standpoint ..
There are algorithms for converting making an xml always look the same, they are called
canonicalization algorithms. .Net has support for canonicalization.
I wrote a small library with asserts for serialization, source.
Sample:
[Test]
public void Foo()
{
...
XmlAssert.Equal(expected, actual, XmlAssertOptions.IgnoreDeclaration | XmlAssertOptions.IgnoreNamespaces);
}
Because of the contents of an XML file can have different formatting and still be considered the same (from a DOM point of view) when you are testing the equality you need to determine what the measure of that equality is, for example is formatting ignored? does meta-data get ignored etc is positioning important, lots of edge cases.
Generally you would create a class that defines your equality rules and use it for your comparisons, and if your comparison class implements the IEqualityComparer and/or IEqualityComparer<T> interfaces, then your class can be used in a bunch of inbuilt framework lists as the equality test implementation as well. Plus of course you can have as many as you need to measure equality differently as your requirements require.
i.e
IEnumerable<T>.Contains
IEnumerable<T>.Equals
The constructior of a Dictionary etc etc
I ended up getting the result I wanted with the following code:
private static void ValidateResult(string validationXml, XPathNodeIterator iterator, params string[] excludedElements)
{
while (iterator.MoveNext())
{
if (!((IList<string>)excludedElements).Contains(iterator.Current.Name))
{
Assert.IsTrue(validationXml.Contains(iterator.Current.Value), "{0} is not the right value for {1}.", iterator.Current.Value, iterator.Current.Name);
}
}
}
Before calling the method, I create a navigator on the instance of XmlDocument this way:
XPathNavigator nav = xdoc.CreateNavigator();
Next, I create an instance of XPathExpression, like so:
XPathExpression expression = XPathExpression.Compile("/blah/*");
I call the method after creating an iterator with the expression:
XPathNodeIterator iterator = nav.Select(expression);
I'm still figuring out how to optimize it further, but it does the trick for now.
I made a method to create simple XML paths.
static XElement MakeFromXPath(string xpath)
{
XElement root = null;
XElement parent = null;
var splits = xpath.Split('/'); //split xpath into parts
foreach (var split in splits)
{
var el = new XElement(split);
if (parent != null)
parent.Add(el);
else
root = el; //first element created, set as root
parent = el;
}
return root;
}
Sample usage:
var element = MakeFromXPath("My/Path/To/Element")'
element will contain the value:
<My>
<Path>
<To>
<Element></Element>
</To>
</Path>
</My>

Categories