Can't successfully clone XMLReader (Unexpected EOF) - c#

I have a really strange problem with XMLReader/XMLTextReader classes.
I have a simple file load:
public void First()
{
XmlTextReader reader = new XmlTextReader(#"C:\MyXMLFile.xml");
XmlReader readerToSerialize;
XmlReader readerToLoad;
DuplicateReaders(reader, out readerToSerialize, out readerToLoad);
XmlSerializer serializer = new XmlSerializer(typeof(XMLTree));
XmlFeed = (XMLDescriptor)serializer.Deserialize(readerToSerialize);
xmlDoc.Load(readerToLoad);
}
protected void DuplicateReaders(XmlTextReader xmlReader, out XmlReader cloneOne, out readerToLoad)
{
XmlDocument _XmlDocument = new XmlDocument();
MemoryStream _Stream = new MemoryStream();
_XmlDocument.Load((XmlTextReader)xmlReader);
_XmlDocument.Save(_Stream);
_Stream.Position = 0L;
cloneOne = XmlReader.Create(_Stream);
_Stream.Position = 0L;
cloneTwo = XmlReader.Create(_Stream);
}
The problem is that only one of the cloned elements read the whole file successully, the next one (xmlDoc.Load) fails always at the same place (Line 91, Character 37 with this xml file). If I directly assign to xmlDoc (i.e. clone the original element only once and asign it directly from the function):
public void First()
{
XmlTextReader reader = new XmlTextReader(#"C:\MyXMLFile.xml");
XmlReader readerToSerialize;
DuplicateReaders(reader, out readerToSerialize);
XmlSerializer serializer = new XmlSerializer(typeof(XMLTree));
XmlFeed = (XMLDescriptor)serializer.Deserialize(readerToSerialize);
}
protected void DuplicateReaders(XmlTextReader xmlReader, out XmlReader cloneOne)
{
XmlDocument _XmlDocument = new XmlDocument();
MemoryStream _Stream = new MemoryStream();
_XmlDocument.Load((XmlTextReader)xmlReader);
_XmlDocument.Save(_Stream);
_Stream.Position = 0L;
cloneOne = XmlReader.Create(_Stream);
_Stream.Position = 0L;
this.xmlDoc.Load(_Stream);
}
I still get the same error 91/37 (Unexpected EOF), but this time in the Serializer.
My initial problem was that if I use xmlDoc.Load(reader) the reader instance get destroyed and I can't serialize it later on. I found the Duplicate function on the MSDN forums, but it's still a no go. What I want to achieve is quite simple:
Use only one reader and get one XmlDocument and one Serialized Class. How hard can it be?

You need to close the first reader before you can use the duplicate.
reader.Close()

Your both cloneOne and cloneTwo use the same underlying memory stream.
use a different MemoryStream
cloneTwo = XmlReader.Create(new MemoryStream(_Stream.ToArray()));

Found much easier solution, instead of cloning the two readers, i just use create a second one from XmlDoc and use it to deserialize.

Related

XmlDocument not preserving whitespace

XmlDocument is adding a space at the end of self closing tags, even with PreserveWhitespace set to true.
// This fails
string originalXml = "<sample><node id=\"99\"/></sample>";
// Convert to XML
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(originalXml);
// Save back to a string
string extractedXml = null;
using (MemoryStream stream = new MemoryStream())
{
doc.Save(stream);
stream.Position = 0;
using(StreamReader reader = new StreamReader(stream))
{
extractedXml = reader.ReadToEnd();
}
}
// Confirm that they are identical
Assert.AreEqual(originalXml, extractedXml);
The desired output is:
<sample><node id="99"/></sample>
But I am getting:
<sample><node id="99" /></sample>
Is there a way to suppress that extra space?
Here's how XmlDocument.Save(Stream) looks like :
public virtual void Save(Stream outStream)
{
XmlDOMTextWriter xmlDomTextWriter = new XmlDOMTextWriter(outStream, this.TextEncoding);
if (!this.preserveWhitespace)
xmlDomTextWriter.Formatting = Formatting.Indented;
this.WriteTo((XmlWriter) xmlDomTextWriter);
xmlDomTextWriter.Flush();
}
So setting PreserveWhiteSpace has no effect on the inside of the nodes. The documentation of the XmlTextWriter says :
When writing an empty element, an additional space is added between tag name and the closing tag, for example . This provides compatibility with older browsers.
So I guess there is no easy way out. Here's a workaround tho:
So I wrote a wrapper class MtxXmlWriter that is derived from XmlWriter and wraps the original XmlWriter returned by XmlWriter.Create() and does all the necessary tricks.
Instead of using XmlWriter.Create() you just call one of the MtxXmlWriter.Create() methods, that's all. All other methods are directly handed over to the encapsulated original XmlWriter except for WriteEndElement(). After calling WriteEndElement() of the encapsulated XmlWriter, " />" is replaced with "/>" in the buffer:

Generate XML and HTML from MemoryStream

Need to generate an html report from XML and corresponding XSL butI have to use memorystream instead of IO File write on server directories. For the most part I managed to create an xml
MemoryStream ms = new MemoryStream();
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = true;
using(XmlWriter writer = XmlWriter.Create(ms,wSettings))
{
/**
creating xml here
**/
writer.Flush();
writer.Close();
}
return ms; // returning the memory stream to another function
// to create html
// This Function creates
protected string ConvertToHtml(MemoryStream xmlOutput)
{
XPathDocument document = new XPathDocument(xmlOutput);
XmlDocument xDoc = new XmlDocument();
xDoc.Load(xmlOutput);
StringWriter writer = new StringWriter();
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(reportDir + "MyXslFile.xsl");
transform.Transform(xDoc, null, writer);
xmlOutput.Position = 1;
StreamReader sr = new StreamReader(xmlOutput);
return sr.RearToEnd();
}
Somewhere along the line I am messing up with creating the HTML Report and cant figure out how to send that file to client end. I dont have much experience working with memorystream. So, any help would be greatly appreciated. Thank you.
You're completely bypassing your transform here:
// This Function creates
protected string ConvertToHtml(MemoryStream xmlOutput)
{
XPathDocument document = new XPathDocument(xmlOutput);
XmlDocument xDoc = new XmlDocument();
xDoc.Load(xmlOutput);
StringWriter writer = new StringWriter();
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(reportDir + "MyXslFile.xsl");
transform.Transform(xDoc, null, writer);
// These lines are the problem
//xmlOutput.Position = 1;
//StreamReader sr = new StreamReader(xmlOutput);
//return sr.RearToEnd();
return writer.ToString()
}
Also, calling Flush right before you call Close on a writer is redundant as Close implies a flush operation.
It is not clear to me what you want to achieve but using both XmlDocument and XPathDocument to load from the same memory stream does not make sense I think. And I would set the MemoryStream to Position 0 before loading from it so either have the function creating and writing to the memory stream ensure that it sets the Position to zero or do that before you call Load on the XmlDocument or before you create an XPathDocument, depending on what input tree model you want to use.

XMLSerializer to XElement

I have been working with XML in database LINQ and find that it is very difficult to work with the serializer.
The database LINQ required a field that store XElement.
I have a complex object with many customized structure class, so I would like to use the XmlSerializer to serialize the object.
However, the serializer can only serialize to file ("C:\xxx\xxx.xml") or a memory stream.
However to convert or serialize it to be a XElement so that I can store in the database using LINQ?
And How to do the reverse? i.e. Deserialize an XElement...
Try to use this
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, value);
stream.Position = 0;
using (XmlReader reader = XmlReader.Create(stream))
{
XElement element = XElement.Load(reader);
}
}
deserialize :
XmlSerializer xs = new XmlSerializer(typeof(XElement));
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, xml);
ms.Position = 0;
xs = new XmlSerializer(typeof(YourType));
object obj = xs.Deserialize(ms);
}
To make what John Saunders was describing more explicit, deserialization is very straightforward:
public static object DeserializeFromXElement(XElement element, Type t)
{
using (XmlReader reader = element.CreateReader())
{
XmlSerializer serializer = new XmlSerializer(t);
return serializer.Deserialize(reader);
}
}
Serialization is a little messier because calling CreateWriter() from an XElement or XDocument creates child elements. (In addition, the XmlWriter created from an XElement has ConformanceLevel.Fragment, which causes XmlSerialize to fail unless you use the workaround here.) As a result, I use an XDocument, since this requires a single element, and gets us around the XmlWriter issue:
public static XElement SerializeToXElement(object o)
{
var doc = new XDocument();
using (XmlWriter writer = doc.CreateWriter())
{
XmlSerializer serializer = new XmlSerializer(o.GetType());
serializer.Serialize(writer, o);
}
return doc.Root;
}
First of all, see Serialize Method to see that the serializer can handle alot more than just memory streams or files.
Second, try using XElement.CreateWriter and then passing the resulting XmlWriter to the serializer.
The SQL has XML data type may be this can help you look at msdn

How to get the output of an XslCompiledTransform into an XmlReader?

I have an XslCompiledTransform object, and I want the output in an XmlReader object, as I need to pass it through a second stylesheet. I'm getting a bit confused - I can successfully transform some XML and read it using either a StreamReader or an XmlDocument, but when I try an XmlReader, I get nothing.
In the example below, stylesheet is my XslCompiledTransform object. The first two Console.WriteLine calls output the correct transformed XML, but the third call gives no XML. I'm guessing it might be that the XmlTextReader is expecting text, so maybe I need to wrap this in a StreamReader..? What am I doing wrong?
MemoryStream transformed = new MemoryStream();
stylesheet.Transform(input, args, transformed);
transformed.Position = 0;
StreamReader s = new StreamReader(transformed);
Console.WriteLine("s = " + s.ReadToEnd()); // writes XML
transformed.Position = 0;
XmlDocument doc = new XmlDocument();
doc.Load(transformed);
Console.WriteLine("doc = " + doc.OuterXml); // writes XML
transformed.Position = 0;
XmlReader reader = new XmlTextReader(transformed);
Console.WriteLine("reader = " + reader.ReadOuterXml()); // no XML written
The XmlReader.ReadOuterXml method reads the XML for the current node. When you first create the reader, there is no current node, so ReadOuterXml will return nothing.
If you add the line:
reader.Read();
...before the ReadOuterXml() call, then it will work as you expect.
P.S. You should normally test the result of the Read() method to ensure that the reader actually has something to read.

Problem with XElement and XslCompiledTransform

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!

Categories