How to create an XML document from a .NET object? - c#

I have the following variable that accepts a file name:
var xtr = new XmlTextReader(xmlFileName) { WhitespaceHandling = WhitespaceHandling.None };
var xd = new XmlDocument();
xd.Load(xtr);
I would like to change it so that I can pass in an object. I don't want to have to serialize the object to file first.
Is this possible?
Update:
My original intentions were to take an xml document, merge some xslt (stored in a file), then output and return html... like this:
public string TransformXml(string xmlFileName, string xslFileName)
{
var xtr = new XmlTextReader(xmlFileName) { WhitespaceHandling = WhitespaceHandling.None };
var xd = new XmlDocument();
xd.Load(xtr);
var xslt = new System.Xml.Xsl.XslCompiledTransform();
xslt.Load(xslFileName);
var stm = new MemoryStream();
xslt.Transform(xd, null, stm);
stm.Position = 1;
var sr = new StreamReader(stm);
xtr.Close();
return sr.ReadToEnd();
}
In the above code I am reading in the xml from a file. Now what I would like to do is just work with the object, before it was serialized to the file.
So let me illustrate my problem using code
public string TransformXMLFromObject(myObjType myobj , string xsltFileName)
{
// Notice the xslt stays the same.
// Its in these next few lines that I can't figure out how to load the xml document (xd) from an object, and not from a file....
var xtr = new XmlTextReader(xmlFileName) { WhitespaceHandling = WhitespaceHandling.None };
var xd = new XmlDocument();
xd.Load(xtr);
}

You want to turn an arbitrary .NET object into a serialized XML string? Nothing simpler than that!! :-)
public string SerializeToXml(object input)
{
XmlSerializer ser = new XmlSerializer(input.GetType(), "http://schemas.yournamespace.com");
string result = string.Empty;
using(MemoryStream memStm = new MemoryStream())
{
ser.Serialize(memStm, input);
memStm.Position = 0;
result = new StreamReader(memStm).ReadToEnd();
}
return result;
}
That should to it :-) Of course you might want to make the default XML namespace configurable as a parameter, too.
Or do you want to be able to create an XmlDocument on top of an existing object?
public XmlDocument SerializeToXmlDocument(object input)
{
XmlSerializer ser = new XmlSerializer(input.GetType(), "http://schemas.yournamespace.com");
XmlDocument xd = null;
using(MemoryStream memStm = new MemoryStream())
{
ser.Serialize(memStm, input);
memStm.Position = 0;
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
using(var xtr = XmlReader.Create(memStm, settings))
{
xd = new XmlDocument();
xd.Load(xtr);
}
}
return xd;
}

You can serialize directly into the XmlDocument:
XmlDocument doc = new XmlDocument();
XPathNavigator nav = doc.CreateNavigator();
using (XmlWriter w = nav.AppendChild())
{
XmlSerializer ser = new XmlSerializer(typeof(MyType));
ser.Serialize(w, myObject);
}

Expanding on #JohnSaunders solution I wrote the following generic function:
public XmlDocument SerializeToXml<T>(T source) {
var document = new XmlDocument();
var navigator = document.CreateNavigator();
using (var writer = navigator.AppendChild()) {
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(writer, source);
}
return document;
}

Related

How To convert an Object Of A Class in xml in c#

I have tried this code but I'm not getting it. Please explain this code:
public static XmlDocument ConvertToXml(object list)
{
XmlDocument xmldoc = new XmlDocument();
XmlSerializer _XmlSerializer = new XmlSerializer(list.GetType());
using (MemoryStream xmlStream = new MemoryStream())
{
_XmlSerializer.Serialize(xmlStream, list);
xmlStream.Position = 0;
xmldoc.Load(xmlStream);
return xmldoc;
}
}
Your code should work.
You can use Generic function like this.
public static XmlDocument ObjectToXmlDocument<T>(T obj)
{
XmlDocument xmldoc = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, obj);
stream.Position = 0;
xmldoc.Load(stream);
}
return xmldoc;
}

How to modify the XML Stream generated from an XmlWriter

I need to take the XML generated by a XmlWriter and modify it. Is it possible?
Being "writer" the XmlWriter, "writeStream" an IOStream and "value" a List of items, here is the code to write with XmlWriter:
writer.WriteRaw("<array>");
writer.WriteRaw("<curLength>" + ((IList)value).Count + "</curLength>");
writer.Close();
serializer = new XmlSerializer(type);
XmlWriter loopWriter;
foreach (object o in (IList)value)
{
loopWriter = XmlWriter.Create(writeStream, new XmlWriterSettings { OmitXmlDeclaration = true });
serializer = new XmlSerializer(o.GetType());
serializer.Serialize(loopWriter, o);
loopWriter.Close();
}
writer = CreateXmlWriter(writeStream, content);
writer.WriteRaw("</array>");
writer.Close();
And after that i want to take the stream generated by the XmlWriter and modify it, doing something like this:
XmlDocument doc = new XmlDocument();
doc.Load(writeStream);
var elements = doc.GetElementsByTagName("struct");
int numberOfElements = elements.Count;
for(int i=1; i<=numberOfElements; i++)
{
XmlNode element = elements.Item(i);
XmlElement indexElement = doc.CreateElement("index");
indexElement.InnerText = i.ToString();
element.InsertAfter(indexElement,element);
}
But, if i execute the code, it throws a "NotSupportedException" at the line of "doc.Load(writeStream);"
Thank you!

XSLT transformation for Word

I'm writing a web service in .NET C# that takes in an object, converts it to xml, applies an XSLT template, runs the transformation, and returns an MS work file.
Here is the code for the function:
public static HttpResponseMessage Transform(object data)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
StringWriter stringWriter = new StringWriter();
XmlWriter xmlWriter = XmlWriter.Create(stringWriter);
var applicationDirectory = AppDomain.CurrentDomain.BaseDirectory;
var xsltPath = applicationDirectory + #"\Reporting\Files\Template.xslt";
var templatePath = applicationDirectory + #"\Reporting\Files\Template.docx";
var xmlObject = new System.Xml.Serialization.XmlSerializer(data.GetType());
MemoryStream stream;
using (stream = new MemoryStream())
{
var sw = new StreamWriter(stream);
xmlObject.Serialize(stream, data);
stream.Position = 0;
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xsltPath);
using (XmlReader xmlReader = XmlReader.Create(stream))
{
transform.Transform(xmlReader, xmlWriter);
XmlDocument newWordContent = new XmlDocument();
newWordContent.LoadXml(stringWriter.ToString());
var outputPath = applicationDirectory + #"\Reporting\Temp\temp.docx";
System.IO.File.Copy(templatePath, outputPath, true);
using (WordprocessingDocument output = WordprocessingDocument.Open(outputPath, true))
{
Body updatedBodyContent = new Body(newWordContent.DocumentElement.InnerXml);
output.MainDocumentPart.Document.Body = updatedBodyContent;
output.MainDocumentPart.Document.Save();
}
response.Content = new StreamContent(new FileStream(outputPath, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = outputPath;
}
}
return response;
}
When I make a request, it gives me a word file without the data.
I put a breakpoint at using (XmlReader xmlReader = XmlReader.Create(stream)).
After running that line, xmlReader has a value of {None}.
I'm also trying to avoid creating an XML file for efficiency(Hence MemoryStream).
Any idea why this isn't working? And is there a better way of accomplishing this?
Thanks,
Gerson
What happens if you change this:
Body updatedBodyContent = new Body(newWordContent.DocumentElement.InnerXml);
to this:
Body updatedBodyContent = new Body(newWordContent.InnerXml);
or this:
Body updatedBodyContent = new Body(newWordContent.DocumentElement.OuterXml);
The way you have it there would cause the outer element of the transformed XML to be omitted, and I doubt that's what you want (though can't say for sure because you've shown us neither the input XML or the XSLT.

Steps to create XML using XSD and post to URL

I was given a XSD file and sample XML file, and asked to post the XML file to a URL.
Sample XML file
<?xml version="1.0"?>
<pingRequest>
<elt></elt>
...
</pingRequest>
I'm familiar with SOAP and REST, but I have never done posting pure XML file directly. Here is what I got so far.
1) Generate C# class from XSD file
xsd.exe Test.xsd /c
2) Serialize from C# class to XML using XmlSerializer
public string SerializeObject(object obj, Type type)
{
string xml;
var xs = new XmlSerializer(type);
using (var ms = new MemoryStream())
{
xs.Serialize(ms, obj, null);
ms.Position = 0;
using (var sr = new StreamReader(memoryStream))
{
xml = sr.ReadToEnd();
}
}
return xml;
}
OR Should I use LINQ to XML to generate XML file?
3) Post XML to URL using WebClient
var client = new WebClient();
var uri = new Uri("http://www.site.com/");
string responseXML = client.UploadString(uri, requestXML);
Am I at the right track? If not, could you please point me to a right direction? Thanks in advance.
Here is my partial code so that other can use it.
First, created two classes based on XML tags using xsd.exe Test.xsd /c (for both request and response), so that I do not have to prase the XML files manually.
public pingResponse Send()
{
var pingRequest = new pingRequest
{
myelement = "test"
};
// Serialize pingRequest class to xml
var serializer = new Serializer();
string requestXml = serializer.SerializeObject(pingRequest, typeof(pingRequest));
// Post xml
var client = new WebClient();
var uri = new Uri("http://www.site.com/");
string responseXML = client.UploadString(uri, requestXML);
return (pingResponse)serializer.DeserializeObject(xml, typeof(Response));
}
public class Serializer
{
public string SerializeObject(object obj, Type type)
{
var setting = new XmlWriterSettings() {OmitXmlDeclaration = true, Indent = true};
var xml = new StringBuilder();
using (var writer = XmlWriter.Create(xml, setting))
{
var nsSerializer = new XmlSerializerNamespaces();
nsSerializer.Add(string.Empty, string.Empty);
var xmlSerializer = new XmlSerializer(type);
xmlSerializer.Serialize(writer, obj, nsSerializer);
}
return xml.ToString();
}
public object DeserializeObject(string xml, Type type)
{
var xs = new XmlSerializer(type);
var stringReader = new StringReader(xml);
var obj = xs.Deserialize(stringReader);
stringReader.Close();
return obj;
}
}
Note: I do not include the PingRequest and PingResponse classes since my member variables will not be same as yours.

Serializing WITHOUT xmlns

I have a couple extension methods that handle serialization of my classes, and since it can be a time consuming process, they are created once per class, and handed out by this method.
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
xmlAttributes.Xmlns = false;
xmlAttributeOverrides.Add(typeOfT, xmlAttributes);
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
This is called by the extension method .Serialize()
public static XElement Serialize(this object source)
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(source.GetType());
var xdoc = new XDocument();
using (var writer = xdoc.CreateWriter())
{
serializer.Serialize(writer, source, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }));
}
return (xdoc.Document != null) ? xdoc.Document.Root : new XElement("Error", "Document Missing");
}
catch (Exception x)
{
return new XElement("Error", x.ToString());
}
}
Unfortunately, when Serializing classes that are auto-generated, they have the attribute XmlTypeAttribute(Namespace="http://tempuri.org/") applied to them.
This causes the deserialization by the the non-auto-generated counterparts to fail.
I need the serializer to completely ignore and not apply the namespace, but what I have written in the first block of code doesn't seem to remove it, I still end up with xml like this
<Note>
<ID xmlns="http://tempuri.org/">12</ID>
<Author xmlns="http://tempuri.org/">
<ID>1234</ID>
<Type>Associate</Type>
<IsAvailable>false</IsAvailable>
</Author>
<Created xmlns="http://tempuri.org/">2010-06-22T09:38:01.5024351-05:00</Created>
<Text xmlns="http://tempuri.org/">This is an update</Text>
</Note>
Instead of the same, minus the xmlns="http://tempuri.org/" attribute.
Please help, thanks, this is driving me crazy!
EDIT:
I know the problem, just not how to fix it.
My class, isn't just full of simple types.
It contains properties with types of other classes. Which are also auto-generated with the XmlTypeAttribute(Namespace = "http://tempuri.org/") attribute. So what is happening, is that when serialization occurs, and it serializes the properties of my class, they aren't going through my custom serialization, and thus, are having the attribute applied and not overridden.
Now I just need to figure out how to jump that hoop. Any thoughts as to how?
EDIT 2:
The following works to serialize WITHOUT xmlns... but I'm having a problem on the deserialization end, just not yet sure if it's related or not
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
xmlAttributes.XmlType = new XmlTypeAttribute
{
Namespace = ""
};
xmlAttributes.Xmlns = false;
var types = new List<Type> {typeOfT, typeOfT.BaseType};
foreach (var property in typeOfT.GetProperties())
{
types.Add(property.PropertyType);
}
types.RemoveAll(t => t.ToString().StartsWith("System."));
foreach (var type in types)
{
xmlAttributeOverrides.Add(type, xmlAttributes);
}
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
//var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides, extraTypes.ToArray(), new XmlRootAttribute(), string.Empty);
//var newSerializer = new XmlSerializer(typeOfT, string.Empty);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
EDIT3:
Ended up using solution from
How to remove all namespaces from XML with C#?
public static XElement RemoveAllNamespaces(this XElement source)
{
return !source.HasElements
? new XElement(source.Name.LocalName)
{
Value = source.Value
}
: new XElement(source.Name.LocalName, source.Elements().Select(el => RemoveAllNamespaces(el)));
}
A working solution, for the record!
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);
No problem - just pass an empty string as the default namespace to the XML serializer:
XmlSerializer newSerializer =
new XmlSerializer(typeOfT, "");
Unfortunately, there's no easy constructor overload if you really need to define the XmlAttributeOverrides and the default namespace - so either you can skip the XmlAttributeOverrides and use that constructor I mentioned, or you need to use the one that defines all possible parameters (including XmlAttributeOverrides and default XML namespaces - and a few more).
public static byte[] SerializeByteByType(object objectToSerialize, Type type)
{
XmlWriterSettings xmlSetting = new XmlWriterSettings()
{
NewLineOnAttributes = false,
OmitXmlDeclaration = true,
Indent = false,
NewLineHandling = NewLineHandling.None,
Encoding = Encoding.UTF8,
NamespaceHandling = NamespaceHandling.OmitDuplicates
};
using (MemoryStream stm = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(stm, xmlSetting))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
xmlAttributes.Xmlns = false;
xmlAttributes.XmlType = new XmlTypeAttribute() { Namespace = "" };
xmlAttributeOverrides.Add(type, xmlAttributes);
XmlSerializer serializer = new XmlSerializer(type, xmlAttributeOverrides);
//Use the following to serialize without namespaces
XmlSerializerNamespaces xmlSrzNamespace = new XmlSerializerNamespaces();
xmlSrzNamespace.Add("", "");
serializer.Serialize(writer, objectToSerialize, xmlSrzNamespace);
stm.Flush();
stm.Position = 0;
}
return stm.ToArray();
}
}
public static string SerializeToXml(object obj)
{
UTF8Encoding encoding = new UTF8Encoding(false);
var xmlAttributes = new XmlAttributes();
xmlAttributes.Xmlns = false;
xmlAttributes.XmlType = new XmlTypeAttribute() { Namespace = "" };
var xmlAttributeOverrides = new XmlAttributeOverrides();
var types = obj.GetType().Assembly.GetTypes().Where(t => string.Equals(t.Namespace, obj.GetType().Namespace, StringComparison.Ordinal));
foreach (var t in types) xmlAttributeOverrides.Add(t, xmlAttributes);
XmlSerializer sr = new XmlSerializer(obj.GetType(), xmlAttributeOverrides);
MemoryStream memoryStream = new MemoryStream();
StreamWriter writer = new StreamWriter(memoryStream, encoding);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
// get the stream from the writer
memoryStream = writer.BaseStream as MemoryStream;
sr.Serialize(writer, obj, namespaces);
// apply encoding to the stream
return (encoding.GetString(memoryStream.ToArray()).Trim());
}
It will even work for complex object containing nested object
Credits to user3444796 and Mentor
Working solution (without empty xmlns attribute):
var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
XmlSerializer serializer = new XmlSerializer(typeof(XmlInfo));
XmlDocument doc = new XmlDocument();
using (var ms = new MemoryStream()) {
serializer.Serialize(ms, this, ns);
ms.Position = 0;
doc.Load(ms);
}

Categories