Cant seem to find a definite answer.
I am wondering if after initializing a XPathDocument with a StringReader and getting a XPathNavigator will I get errors if I use the Navigator after I dispose of the StringReader?
Here is an example.
XPathDocument doc = null;
XPathNavigator nav = null;
using (var reader = new StringReader(config))
{
doc = new XPathDocument(reader);
nav = doc.CreateNavigator();
nav.MoveToFirstChild();
}
var test = nav.SelectSingleNode("testNode");
If I continue to use this nav object will I get an error since the reader will have been disposed?
You will be able to continue to use nav. All StringReader does is read a config file, which gets passed to your XPathDocument doc. After that it's job is done, it's data has all been used.
Related
I've to make an upgrade mechanism which will update an XML documents(To another xml document).
The signature of the method that I've to respect is :
public XmlDocument Update(XmlDocument sourceDocument){...}
What would be the most efficient way to apply an XSLT file on this?
I was expecting to be able to use the XslTransform class, but it only accept stream and XmlWriter as parameter for the output.
So I know that I could do something like:
public XmlDocument Update(XmlDocument sourceDocument){
XslTransform myXslTransform = new XslTransform();
myXslTransform.Load("myXsl.xsl");
MemoryStream ms = new MemoryStream();
myXslTransform.Transform(sourceDocument, null, ms);
XmlDocument output = new XmlDocument();
output.Load(ms);
return output;
}
But I find this not very efficient(knowing that my XSLT will be to rename some nodes, add a node in-between, add a child). Is there a way to do better?
My "only" constraints are: Input/Output: XmlDocument, External XSLT to load.
If you want to use a System.Xml.XmlDocument with the current XSLT 1.0 implementation (XslCompiledTransform) that Microsoft offers then you can use
XmlDocument resultDocument = new XmlDocument();
using (XmlWriter xw = resultDocument.CreateNavigator().AppendChild()) {
XslCompiledTransform proc = new XslCompiledTransform();
proc.Load("myXsl.xsl");
proc.Transform(sourceDocument, null, xw);
xw.Close();
}
return resultDocument;
I created an application for Android in Xamarin.I open an xml file ,then I save the changes. I need to close it but xmldocument has not any option to do that.
How can I close the file? I really need to do this because on other activity I want to modify it and it says :"Sharing violation on path"
If you're using the XmlDocument.Save just put a using around the TextWriter should do it:
var doc = new XmlDocument();
var filePath = "myXmlFile.xml";
doc.Load(filePath);
using (var writer = new StreamWriter(filePath))
{
doc.Save(writer);
}
I have an XML file with no root. I cannot change this. I am trying to parse it, but XDocument.Load won't do it. I have tried to set ConformanceLevel.Fragment, but I still get an exception thrown. Does anyone have a solution to this?
I tried with XmlReader, but things are messed up and can't get it work right. XDocument.Load works great, but if I have a file with multiple roots, it doesn't.
XmlReader itself does support reading of xml fragment - i.e.
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var reader = XmlReader.Create("fragment.xml", settings))
{
// you can work with reader just fine
}
However XDocument.Load does not support reading of fragmented xml.
Quick and dirty way is to wrap the nodes under one virtual root before you invoke the XDocument.Parse. Like:
var fragments = File.ReadAllText("fragment.xml");
var myRootedXml = "<root>" + fragments + "</root>";
var doc = XDocument.Parse(myRootedXml);
This approach is limited to small xml files - as you have to read file into memory first; and concatenating large string means moving large objects in memory - which is best avoided.
If performance matters you should be reading nodes into XDocument one-by-one via XmlReader as explained in excellent #Martin-Honnen 's answer (https://stackoverflow.com/a/18203952/2440262)
If you use API that takes for granted that XmlReader iterates over valid xml, and performance matters, you can use joined-stream approach instead:
using (var jointStream = new MultiStream())
using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes("<root>"), false))
using (var fileStream =
File.Open(#"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes("</root>"), false))
{
jointStream.AddStream(openTagStream);
jointStream.AddStream(fileStream);
jointStream.AddStream(closeTagStream);
using (var reader = XmlReader.Create(jointStream))
{
// now you can work with reader as if it is reading valid xml
}
}
MultiStream - see for example https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685
Note: XDocument loads the whole xml into memory. So don't use it for large files - instead use XmlReader for iteration and load just the crispy bits as XElement via XNode.ReadFrom(...)
The only in-memory tree representations in the .NET framework that can deal with fragments are the XmlDocumentFragment in .NET's DOM implementation so you would need to create an XmlDocument and a fragment with e.g.
XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = stringWithXml; // for instance
// frag.InnerXml = File.ReadAllText("fragment.xml");
or is XPathDocument where you can create one using an XmlReader with ConformanceLevel set to Fragment:
XPathDocument doc;
using (XmlReader xr =
XmlReader.Create("fragment.xml",
new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
}))
{
doc = new XPathDocument(xr);
}
// new create XPathNavigator for read out data e.g.
XPathNavigator nav = doc.CreateNavigator();
Obviously XPathNavigator is read-only.
If you want to use LINQ to XML then I agree with the suggestions made that you need to create an XElement as a wrapper. Instead of pulling in a string with the file contents you could however use XNode.ReadFrom with an XmlReader e.g.
public static class MyExtensions
{
public static IEnumerable<XNode> ParseFragment(XmlReader xr)
{
xr.MoveToContent();
XNode node;
while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null)
{
yield return node;
}
}
}
then
XElement root = new XElement("root",
MyExtensions.ParseFragment(XmlReader.Create(
"fragment.xml",
new XmlReaderSettings() {
ConformanceLevel = ConformanceLevel.Fragment })));
That might work better and more efficiently than reading everything into a string.
If you wanted to use XmlDocument.Load() then you would need to wrap the content in a root node.
or you could try something like this...
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
XmlDocument d = new XmlDocument();
d.CreateElement().InnerText = xmlReader.ReadOuterXml();
}
}
XML document cannot have more than one root elements. One root element is required. You may do one thing. Get all the fragment elements and wrap them into a root element and parse it with XDocument.
This would be the best and easiest approach that one could think of.
how do I close this document that was called this way:
var xmlDoc = XDocument.Load(new XmlTextReader(Server.MapPath("Nc.xml")));
thanks
XmlTextReader implements IDisposable. In general, you should call IDisposable.Dispose() as soon as you no longer need the resource to allow the system to close open handles, etc.
The best use pattern for IDisposable is to use the using syntax, which will call IDisposable.Dispose() automatically in an implicit try..finally wrapper:
using (var reader = new XmlTextReader(Server.MapPath("Nc.xml")))
{
var xdoc = XDocument.Load(reader);
{ .. do xdoc work here .. }
} // reader disposed here
or if you want to keep the xdoc around a long time for other work but want to close the file as soon as possible, do it this way:
XDocument xdoc = null;
using (var reader = new XmlTextReader(Server.MapPath("Nc.xml")))
{
xdoc = XDocument.Load(reader);
} // reader disposed here
{ .. do xdoc work here .. }
Once the reader is done, it will close the what it has read automatically.
otherwise hang the reference out for GC by
xmlDoc = null;
which will tear down any internal open items.
I'm having some trouble using a combination of XElement and XslCompiledTransform. I've put the sample code I'm using below. If I get my input XML using the GetXmlDocumentXml() method, it works fine. If I use the GetXElementXml() method instead, I get an InvalidOperationException when calling the Transform method of XslComiledTransform:
Token Text in state Start would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment.
The CreateNavigator method on both XElement and XmlDocument returns an XPathNavigator. What extra stuff is XmlDocument doing so this all works, and how can I do the same with XElement? Am I just doing something insane?
static void Main(string[] args)
{
XslCompiledTransform stylesheet = GetStylesheet(); // not shown for brevity
IXPathNavigable input = this.GetXElementXml();
using (MemoryStream ms = this.TransformXml(input, stylesheet))
{
XmlReader xr = XmlReader.Create(ms);
xr.MoveToContent();
}
}
private MemoryStream TransformXml(
IXPathNavigable xml,
XslCompiledTransform stylesheet)
{
MemoryStream transformed = new MemoryStream();
XmlWriter writer = XmlWriter.Create(transformed);
stylesheet.Transform(xml, null, writer);
transformed.Position = 0;
return transformed;
}
private IXPathNavigable GetXElementXml()
{
var xml = new XElement("x", new XElement("y", "sds"));
return xml.CreateNavigator();
}
private IXPathNavigable GetXmlDocumentXml()
{
var xml = new XmlDocument();
xml.LoadXml("<x><y>sds</y></x>");
return xml.CreateNavigator();
}
Oh, that was easy. The solution was to wrap the XElement in an XDocument object. Problem solved!