XslCompiledTransform and Serialization - c#

I am trying to implement some functions that will convert one object to another with XslCompiledTransform.
I found some implementations for Serializing an object to XML string and DeSerialize the XML string to an object.
Another function does the XslCompiledTransform from object1 to obejbct2.
To generate the XSLT file i used the Altova MapForce, just loaded the XML of the serialized objects and mapped some attributes.
Now for the problems:
first I noticed that the XslCompiledTransform doesn't work with XSLT version 2.0. is there any newer functions that do work with XSLT 2.0? maybe some settings?
secondly I get an exception when trying to DeSerialize the XML to an object:
"There was an error deserializing the object of type myObject Input string was not in a correct format."
I don't understand where is the problem.
Does anybody have a sample code that does such a thing? all I find in google are Transformations of HTML code and not objects.
Here are the functions:
private static string runXSLT(string xsltFile, string inputXML)
{
XmlDocument XmlDoc = new XmlDocument();
// Load the style sheet.
XslCompiledTransform xslt = new XslCompiledTransform(true);
xslt.Load(xsltFile);
StringReader StrReader = new StringReader(inputXML);
XmlTextReader XmlReader = new XmlTextReader(StrReader);
//Create an XmlTextWriter which outputs to memory stream
Stream stream = new MemoryStream();
XmlWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
// Execute the transform and output the results to a file.
xslt.Transform(XmlReader, writer);
stream.Position = 0;
XmlDoc.Load(stream);
return XmlDoc.InnerXml;
}
public static string SerializeAnObject(object AnObject)
{
XmlDocument XmlDoc = new XmlDocument();
DataContractSerializer xmlDataContractSerializer = new DataContractSerializer(AnObject.GetType());
MemoryStream MemStream = new MemoryStream();
try
{
xmlDataContractSerializer.WriteObject(MemStream, AnObject);
MemStream.Position = 0;
XmlDoc.Load(MemStream);
return XmlDoc.InnerXml;
}
finally
{
MemStream.Close();
}
}
public static Object DeSerializeAnObject(string XmlOfAnObject, Type ObjectType)
{
StringReader StrReader = new StringReader(XmlOfAnObject);
DataContractSerializer xmlDataContractSerializer = new DataContractSerializer(ObjectType);
XmlTextReader XmlReader = new XmlTextReader(StrReader);
try
{
Object AnObject = xmlDataContractSerializer.ReadObject(XmlReader);
return AnObject;
}
finally
{
XmlReader.Close();
StrReader.Close();
}
}
Thanks allot,
Omri.

XslCompiledTransform does not support XSLT 2.0. In fact, XSLT 2.0 is not supported within the .NET Framework at all (you could try the Saxon version for .NET, but be aware that this is just the Java version running inside IKVM).
From your description I did not understand why you are taking the detour via XML to convert one object into another. Why don't you simply provide a constructor in your target object that takes your input object as a paramater? Then you can code all the mapping inside that constructor. This is not onlyby far more efficient than serializing, transforming and deserializing your objects you will also get the type safety of C#.

Related

Correct way to add a namespace to an element using XmlDocument

I have to modify a incoming SOAP XML message to add a namespace to one of the elements so that the deserialisation will work. However when I add the xmlns attribute to the correct element, I get an error when I try to load the xml in to an XmlWriter via a stream (which I need to do to in my IClientMessageInspector implementation to replace the Message reply).
The prefix '' cannot be redefined from '' to 'http://www.example.com' within the same start element tag.
I have a work around, which is that after I've modifed the attribute, I reload the entire XML document from it's own OuterXML. This works for some reason, but makes me think there must be a 'correct' way to do this.
Here's a sample test that demonstrates the problem and my current solutions:
[Test]
public void XmlNamespaceTest()
{
var originalXmlString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><TestElement><Child>thing</Child></TestElement>";
var desiredXmlString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><TestElement xmlns=\"http://www.example.com\"><Child>thing</Child></TestElement>";
var doc = new XmlDocument();
doc.LoadXml(originalXmlString);
Assert.That(originalXmlString, Is.EqualTo(doc.OuterXml));
// Write this document via an XMLWriter
var ms = new MemoryStream();
var xmlWriter = XmlWriter.Create(ms);
doc.WriteTo(xmlWriter);
xmlWriter.Flush();
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
var originalXmlViaXmlWriter = sr.ReadToEnd();
Assert.That(originalXmlString, Is.EqualTo(originalXmlViaXmlWriter));
// Add the namespace to the element
((XmlElement)doc.GetElementsByTagName("TestElement").Item(0))?.SetAttribute("xmlns", "http://www.example.com");
Assert.That(desiredXmlString, Is.EqualTo(doc.OuterXml));
// Now attempt to write this modified xml via an XMLWriter
ms.SetLength(0);
xmlWriter = XmlWriter.Create(ms);
//Uncomment line below to fix the error.
//doc.LoadXml(doc.OuterXml);
doc.WriteTo(xmlWriter);
xmlWriter.Flush();
ms.Position = 0;
sr = new StreamReader(ms);
var modifedXmlViaXmlWriter = sr.ReadToEnd();
Assert.That(desiredXmlString, Is.EqualTo(modifedXmlViaXmlWriter));
}
According to this you can't change an elements namespace in an XmlDocument. This is also what #LocEngineer found in his comment. The referenced article mentions that the only way to do this is to save and reload the XmlDocument, which is exactly what you are doing.
If you are in a position to use XDoxument instead, it is possible. See this answer for a solution.

How to use XmlDocument object instead of reading XML file from drive?

I didn't know that I can use XSD schema to serialize received XML file. I used xsd.exe to generate cs class from XSD file and now I need to use that class to get data in class properties but I miss one thing and I need help.
This is the code:
private void ParseDataFromXmlDocument_UsingSerializerClass(XmlDocument doc)
{
XmlSerializer ser = new XmlSerializer(typeof(ClassFromXsd));
string filename = Path.Combine("C:\\myxmls\\test", "xmlname.xml");
ClassFromXsdmyClass = ser.Deserialize(new FileStream(filename, FileMode.Open)) as ClassFromXsd;
if (myClass != null)
{
// to do
}
...
Here I use XML file from drive. And I want to use this XmlDocument from parameter that I passed in. So how to adapt this code to use doc instead XML from drive?
You could write the XmlDocument to a MemoryStream, and then Deserialize it like you already did.
XmlDocument doc = new XmlDocument();
ClassFromXsd obj = null;
using (var s = new MemoryStream())
{
doc.Save(s);
var ser = new XmlSerializer(typeof (ClassFromXsd));
s.Seek(0, SeekOrigin.Begin);
obj = (ClassFromXsd)ser.Deserialize(s);
}

Why is XmlSerializer's Deserialize() spitting out a child object which is a XmlNode[]?

I'm using XmlSerializer to serialize and then deserialize a simple object. When I deserialize the object to my surprise I find a child object was not properly deserialized but instead turned into XmlNode[].
Here is very nearly the structure I've got:
// This line I put in here as a way of sneaking into the XML the
// root node's C# namespace, since it's not the same as the
// deserializing code and the deserializing code seemed unable to
// deserialize properly without knowing the Type (see my code below).
// So I basically just use this fake construct to get the namespace
// and make a Type of it to feed the XmlSerializer() instantiation.
[XmlRoot(Namespace = "http://foo.com/CSharpNamespace/Foo.Bar")]
// This is because QueuedFile can be given to the Argument array.
[XmlInclude(typeof(QueuedFile))]
// This class is Foo.Bar.CommandAndArguments
public class CommandAndArguments {
public String Command;
public object[] Arguments;
}
// I don't think this matters to XmlSerialize, but just in case...
[Serializable()]
// I added this line just thinking maybe it would help, but it doesn't
// do anything. I tried it without the XmlType first, and that
// didn't work.
[XmlType("Foo.Baz.Bat.QueuedFile")]
// This class is Foo.Baz.Bat.QueuedFile (in a different c#
// namespace than CommandAndArguments and the deserializing code)
public QueuedFile {
public String FileName;
public String DirectoryName;
}
And the code which deserializes it looks like:
public static object DeserializeXml(String objectToDeserialize)
{
String rootNodeName = "";
String rootNodeNamespace = "";
using (XmlReader xmlReader = XmlReader.Create(new StringReader(objectToDeserialize)))
{
if (xmlReader.MoveToContent() == XmlNodeType.Element)
{
rootNodeName = xmlReader.Name;
rootNodeNamespace = xmlReader.NamespaceURI;
if (rootNodeNamespace.StartsWith("http://foo.com/CSharpNamespace/"))
{
rootNodeName = rootNodeNamespace.Substring("http://foo.com/CSharpNamespace/".Length) + "." +
rootNodeName;
}
}
}
//MessageBox.Show(rootNodeName);
try
{
Type t = DetermineTypeFromName(rootNodeName);
if (t == null)
{
throw new Exception("Could not determine type of serialized string. Type listed as: "+rootNodeName);
}
var s = new XmlSerializer(t);
return s.Deserialize(new StringReader(objectToDeserialize));
// object o = new object();
// MethodInfo castMethod = o.GetType().GetMethod("Cast").MakeGenericMethod(t);
// return castMethod.Invoke(null, new object[] { s.Deserialize(new StringReader(objectToDeserialize)) });
}
catch (InvalidOperationException)
{
return null;
}
}
And here is the XML when the CommandAndArguments is serialized:
<?xml version="1.0" encoding="utf-16"?>
<CommandAndArguments xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://foo.com/CSharpNamespace/Foo.Bar">
<Command>I am a command</Command>
<Arguments>
<anyType xsi:type="Foo.Baz.Bat.QueuedFile">
<FileName xmlns="">HelloWorld.txt</FileName>
<DirectoryName xmlns="">C:\foo\bar</DirectoryName>
</anyType>
</Arguments>
</CommandAndArguments>
But when I deserialize I am given a CommandAndArguments object where Arguments is XmlNode[] with the first item being the attribute giving the QueuedFile as the type and the other indices being elements of the properties. But why wasn't the QueuedFile object recreated?
I suspect this might somehow have do with C# namespaces and the engine doing the deserializing not being able to find or work with QueuedFile... But I don't see why since when I forgot the XmlInclude() it made sure to tell me it didn't expect QueuedFile and now that I've added the XmlInclude() I get no error, just an incomplete deserialization.
Help? I've read everything I can find to read and Googled everything I know to Google and am stuck. I certainly have a lot to learn about XML serialization but I'm not sure how I'm failing at something which should be pretty simple (I actually did something almost exactly like this before without any problem, the only difference then was that everything was in the same C# namespace).
Are you able to change the XML format or is it fixed? I don't know what the problem you are having is, but I use the DataContractSerializer classes extensively with no problems.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
public static void WriteObject(string fileName)
{
Console.WriteLine(
"Creating a Person object and serializing it.");
Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream writer = new FileStream(fileName, FileMode.Create);
DataContractSerializer ser =
new DataContractSerializer(typeof(Person));
ser.WriteObject(writer, p1);
writer.Close();
}
public static void ReadObject(string fileName)
{
Console.WriteLine("Deserializing an instance of the object.");
FileStream fs = new FileStream(fileName,
FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
// Deserialize the data and read it from the instance.
Person deserializedPerson =
(Person)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
Console.WriteLine(String.Format("{0} {1}, ID: {2}",
deserializedPerson.FirstName, deserializedPerson.LastName,
deserializedPerson.ID));
}
To anyone coming along with a similar problem, depending on your situation you're probably better off with NetDataContractSerializer. It is an alternative to DataContractSerializer which records the .Net types in the XML making deserialization a breeze, since it knows exactly what types are involved and thus you do not need to tell it what type the root object is with the deserialize command. And it can produce output in XML or binary form (I prefer XML for easier debugging).
Here is some sample code for easily serializing and deserializing an object to and from a string:
private static object Deserialize(string xml)
{
object toReturn = null;
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var netDataContractSerializer = new NetDataContractSerializer();
toReturn = netDataContractSerializer.Deserialize(stream);
}
return toReturn;
}
private static string Serialize(object obj)
{
using (var memoryStream = new MemoryStream())
using (var reader = new StreamReader(memoryStream))
{
var netDataContractSerializer = new NetDataContractSerializer();
netDataContractSerializer.Serialize(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
Easy as pie!

Process XML in C# using external entity file

I am processing an XML file (which does not contain any dtd or ent declarations) in C# that contains entities such as é and à. I receive the following exception when attempting to load an XML file...
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(record);
Reference to undeclared entity
'eacute'.
I was able to track down the proper ent file here. How do I tell XmlDocument to use this ent file when loading my XML file?
In versions of the framework prior to .Net 4 you use ProhibitDtd of an XmlReaderSettings instance.
var settings = new XmlReaderSettings();
settings.ProhibitDtd = false;
string DTD = #"<!DOCTYPE doc [
<!ENTITY % iso-lat1 PUBLIC ""ISO 8879:1986//ENTITIES Added Latin 1//EN//XML""
""http://www.oasis-open.org/docbook/xmlcharent/0.3/iso-lat1.ent"">
%iso-lat1;
]> ";
string xml = string.Concat(DTD,"<xml><txt>rené</txt></xml>");
XmlDocument xd = new XmlDocument();
xd.Load(XmlReader.Create(new MemoryStream(
UTF8Encoding.UTF8.GetBytes(xml)), settings));
From .Net 4.0 onward use the DtdProcessing property with a value of DtdProcessing.Parse which you set on the XmlTextReader.
XmlDocument xd = new XmlDocument();
using (var rdr = new XmlTextReader(new StringReader(xml)))
{
rdr.DtdProcessing = DtdProcessing.Parse;
xd.Load(rdr);
}
I ran into the same problem, and not wanting to modify my XML (or DTD), I decided to create my own XmlResolver to add entities on the fly.
My implementation actually reads entities from the config file, but this should be enough to do what you're asking for. In this example, I'm converting a right single curly quote into an apostrophe.
class XmlEntityResolver : XmlResolver {
public override object GetEntity(Uri absoluteUri,
string role,
Type ofObjectToReturn)
{
if (absoluteUri.toString() == "-//MY PUB ID") {
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.Write("<!ENTITY rsquo \"'\">");
sw.Flush();
ms.Position = 0;
return ms;
}
else {
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
}
}
Then, when you declare your XmlDocument, just set the resolver prior to load.
XmlDocument doc = new XmlDocument();
doc.XmlResolver = new XmlEntityResolver();
doc.Load(XML_FILE);
é is not a valid XML entity by default whereas it is a valid HTML entity by default.
You would need to define é as a valid XML entity for XML parsing purposes.
EDIT:
To add a reference to your external ent file you need to do that within the XML file itself. Save the ent file to disk and place it within the same directory as the document being parsed.
<!ENTITY % stuff SYSTEM "iso-lat1.ent">
%stuff;
If you want to go a different route check out the information on ENTITY declaration.
According to this, you have to reference them within the file; you cannot tell LoadXml to do this for you.
Your question has been answered in 2004 itself at MSDN Article........ You can find it here.......
http://msdn.microsoft.com/en-us/library/aa302289.aspx

Creating an XML Element object from an XML Writer in C#

I'm writing a Windows service in C#. I've got an XmlWriter which is contains the output of an XSLT transformation. I need to get the XML into an XMLElement object to pass to a web service.
What is the best way to do this?
You do not need an intermediate string, you can create an XmlWriter that writes directly into an XmlNode:
XmlDocument doc = new XmlDocument();
using (XmlWriter xw = doc.CreateNavigator().AppendChild()) {
// Write to `xw` here.
// Nodes written to `xw` will not appear in the document
// until `xw` is closed/disposed.
}
and pass xw as the output of the transform.
NB. Some parts of the xsl:output will be ignored (e.g. encoding) because the XmlDocument will use its own settings.
Well, an XmlWriter doesn't contain the output; typically, you have a backing object (maybe a StringBuilder or MemoryStream) that is the dumping place. In this case, StringBuilder is probably the most efficient... perhaps something like:
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb))
{
// TODO write to writer via xslt
}
string xml = sb.ToString();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlElement el = doc.DocumentElement;
If you provide a writer, you provide a repository where an output generator is transferring data, thus the replay of Richard is good, you don't really need a string builder to send data from a reader to an XmlDocument!

Categories

Resources