Problem with XElement and XslCompiledTransform - c#

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!

Related

Apply XSLT on an XmlDocument and get the updated XmlDocument

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;

XDocument will not parse html entities (e.g. ) but XmlDocument will

I am currently converting our old parsers that run on XmlDocument to the XDocument. I do this mainly to get the Linq querying and the added linenumber info.
My xml contains an element like this:
<?xml version="1.0"?>
<fulltext>
hello this is a failed textnode
and I don't know how to parse it.
</fulltext>
My problem is that while XmlDocument seems to have no problem reading that node with:
var xmlDocument = new XmlDocument();
var physicalPath = GetPhysicalPath(uploadFolderFile);
try
{
xmlDocument.Load(physicalPath);
}
catch (XmlException xmlException)
{
_log.Warn("Problems with the document", xmlException);
}
The example above parses the document fine but when I try to do:
XDocument xmlDocument;
var physicalPath = GetPhysicalPath(uploadFolderFile);
var xmlStream = new System.IO.StreamReader(physicalPath);
try
{
xmlDocument = XDocument.Load(xmlStream, LoadOptions.SetLineInfo | LoadOptions.SetBaseUri);
}
catch (XmlException)
{
_log.Warn("Trying to clean document for HexaDecimal", xmlException);
}
It fails to read the document because of the character
The special character seems to be allowed in XML version 1.1 but changing the description doesn't help.
I have thought about just parsing the document with XmlDocument and then converting it; but that seems to be counterintuitive. Can anybody help with this problem?
Ok...so I sort of found a solution to this problem.
First of all I try to parse the xml using the following code:
private XDocument GetXmlDocument(String physicalPath)
{
XDocument xmlDocument;
var xmlStream = new System.IO.StreamReader(physicalPath);
try
{
xmlDocument = XDocument.Load(xmlStream, LoadOptions.SetLineInfo);
}
catch (XmlException)
{
//_log.Warn("Trying to clean document for HexaDecimal", xmlException);
xmlDocument = XmlSanitizingStream.TryToCleanXMLBeforeParsing(physicalPath);
}
return xmlDocument;
}
If it fails to load the document, then I will try to clean it using the technique used in this blogpost:
http://seattlesoftware.wordpress.com/2008/09/11/hexadecimal-value-0-is-an-invalid-character/
It will not remove the character I mentioned before, but it will remove any character not allowed by the XML standard.
Then, after sanitizing the XML, I add an XMLReader and set its settings to not check characters:
public static XDocument TryToCleanXMLBeforeParsing(String physicalPath)
{
string xml;
Encoding encoding;
using (var reader = new XmlSanitizingStream(File.OpenRead(physicalPath)))
{
xml = reader.ReadToEnd();
encoding = reader.CurrentEncoding;
}
byte[] encodedString;
if (encoding.Equals(Encoding.UTF8)) encodedString = Encoding.UTF8.GetBytes(xml);
else if (encoding.Equals(Encoding.UTF32)) encodedString = Encoding.UTF32.GetBytes(xml);
else encodedString = Encoding.Unicode.GetBytes(xml);
var ms = new MemoryStream(encodedString);
ms.Flush();
ms.Position = 0;
var settings = new XmlReaderSettings {CheckCharacters = false};
XmlReader xmlReader = XmlReader.Create(ms, settings);
var xmlDocument = XDocument.Load(xmlReader);
ms.Close();
return xmlDocument;
}
Since I've cleaned the document removing illegal characters before I add the ignore characters to the reader, I am pretty sure that I do not read a malformed XML document. Worst case scenario is I get a malformed XML and it will throw an error anyways.
I only use this for parsing and it should only be used to read the data. This will not make the XML well-formed and will in many cases throw exceptions elsewhere in your code. I am only using this because I cannot change what the customer is sending us and I have to read it as is.

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

creating new xml file by C#

how we can create new xml file by C# in the following situation:
its winform.
our xml code is in a string str = "<><><><>...etc"
now i want to create an xml file myxml.xml which contains everything in str,
please answer in the context and thanks...
need simple source code
XmlDocument.LoadXml
using System;
using System.Xml;
public class Sample {
public static void Main() {
// Create the XmlDocument.
XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>"); //Your string here
// Save the document to a file and auto-indent the output.
XmlTextWriter writer = new XmlTextWriter("data.xml",null);
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
}
Hi how about creating a method just for this:
private static void CreateXMLFile(string xml, string filePath)
{
// Create the XmlDocument.
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml); //Your string here
// Save the document to a file and auto-indent the output.
XmlTextWriter writer = new XmlTextWriter(filePath, null);
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
If the only thing you want to do is to save the string as a file (and you don't want to do do XML-specific stuff like checking well-formedness or automatic indenting), you can simply use File.WriteAllText().

Categories