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.
Related
I already searched a lot and unable to find a solution and unable to determine the correct approach
I am serializing an object to xml string and deserializing it back to an object using c#. XML string after serialization adds a leading ?. When I dezerialize it back to the object I am getting an error There is an error in XML document (1, 1)
?<?xml version="1.0" encoding="utf-16"?>
Serialization code:
string xmlString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("abc", "http://example.com/abc/");
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream,Encoding.Unicode);
xs.Serialize(xmlTextWriter, obj, ns);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
xmlString = ConvertByteArrayToString(memoryStream.ToArray());
ConvertByteArrayToString:
UnicodeEncoding encoding = new UnicodeEncoding();
string constructedString = encoding.GetString(characters);
Deserialization Code:
XmlSerializer ser = new XmlSerializer(typeof(T));
StringReader stringReader = new StringReader(xml);
XmlTextReader xmlReader = new XmlTextReader(stringReader);
object obj = ser.Deserialize(xmlReader);
xmlReader.Close();
stringReader.Close();
return (T)obj;
I would like to know what I am doing wrong with encoding and I need a solution that works for most cases. Thanks
Use following function for serialization and Deserialization
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
Your serialized XML contains a Unicode byte-order mark in the beginning, and this is where the deserializer fails.
To remove the BOM you need to create a different version of encoding suppressing BOM instead of using default Encoding.Unicode:
new XmlTextWriter(memoryStream, new UnicodeEncoding(false, false))
Here the second false prevents BOM being prepended to the string.
I'm working on some code to read an XML fragment which contains an XML declaration, e.g. <?xml version="1.0" encoding="utf-8"?> and parse the encoding. From MSDN, I should be able to do it like this:
var nt = new NameTable();
var mgr = new XmlNamespaceManager(nt);
var context = new XmlParserContext(null, mgr, null, XmlSpace.None);
var reader = new System.Xml.XmlTextReader(#"<?xml version=""1.0"" encoding=""UTF-8""?>",
System.Xml.XmlNodeType.XmlDeclaration, context);
However, I'm getting a System.Xml.XmlException on the call to the System.Xml.XmlTextReader constructor with an error message:
XmlNodeType XmlDeclaration is not supported for partial content
parsing.
I've googled this error in quotes -- exactly zero results found (edit: now there's one result: this post) -- and without quotes, which yields nothing useful. I've also looked at MSDN for the XmlNodeType, and it doesn't say anything about it not being supported.
What am I missing here? How can I get an XmlTextReader instance from an XML declaration fragment?
Note, my goal here is just to determine the encoding of a partially-built XML document where I'm making the assumption that it at least contains a declaration node; thus, I'm trying to get reader.Encoding. If there's another way to do that, I'm open to that.
At present, I'm parsing the declaration manually using regex, which is not the best approach.
Update: Getting the encoding from XML documentation or from XML fragment:
Here's a way to get the encoding without having to resort to fake root, using XmlReader.Create.
private static string GetXmlEncoding(string xmlString)
{
if (string.IsNullOrWhiteSpace(xmlString)) throw new ArgumentException("The provided string value is null or empty.");
using (var stringReader = new StringReader(xmlString))
{
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var xmlReader = XmlReader.Create(stringReader, settings))
{
if (!xmlReader.Read()) throw new ArgumentException(
"The provided XML string does not contain enough data to be valid XML (see https://msdn.microsoft.com/en-us/library/system.xml.xmlreader.read)");
var result = xmlReader.GetAttribute("encoding");
return result;
}
}
}
Here's the output, with a full and fragment XML:
If you want to have System.Text.Encoding, you can modify the code to look like this:
private static Encoding GetXmlEncoding(string xmlString)
{
using (StringReader stringReader = new StringReader(xmlString))
{
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
var reader = XmlReader.Create(stringReader, settings);
reader.Read();
var encoding = reader.GetAttribute("encoding");
var result = Encoding.GetEncoding(encoding);
return result;
}
}
Old answer:
As you mentioned, XmlTextReader's Encoding-property contains the encoding.
Here's a full Console app's source code which hopefully is useful:
class Program
{
static void Main(string[] args)
{
var asciiXML = #"<?xml version=""1.0"" encoding=""ASCII""?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>";
var utf8XML = #"<?xml version=""1.0"" encoding=""UTF-8""?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>";
var asciiResult = GetXmlEncoding(asciiXML);
var utfResult = GetXmlEncoding(utf8XML);
Console.WriteLine(asciiResult);
Console.WriteLine(utfResult);
Console.ReadLine();
}
private static Encoding GetXmlEncoding(string s)
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(s));
using (var xmlreader = new XmlTextReader(stream))
{
xmlreader.MoveToContent();
var encoding = xmlreader.Encoding;
return encoding;
}
}
}
Here's the output from the program:
If you know that the XML only contains the declaration, maybe you can add an empty root? So for example:
var fragmentResult = GetXmlEncoding(xmlFragment + "<root/>");
Good evening, here's the solution with a System.Text.Encoding as output.
I made it to be clear, and step by step.
class Program
{
static void Main(string[] args)
{
var line = File.ReadLines(YourFileName).First();
var correctXml = line + "<Root></Root>";
var xml = XDocument.Parse(correctXml);
var stringEncoding = xml.Declaration.Encoding;
var encoding = System.Text.Encoding.GetEncoding(stringEncoding);
}
}
Maybe late but you can use below code after loading it in a XmlDocument
static string getEncoding(XmlDocument xml)
{
if (xml.FirstChild.NodeType == XmlNodeType.XmlDeclaration)
{
return (xml.FirstChild as XmlDeclaration).Encoding;
}
return "utf-8";
}
If you have a byte array as input, try something like this:
private Encoding getEncoding(byte[] data)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Ignore;
XmlDocument doc = new XmlDocument();
MemoryStream ms = new MemoryStream(data);
XmlReader reader = XmlReader.Create(ms, settings);
doc.Load(reader);
XmlDeclaration declaration = doc.ChildNodes.OfType<XmlDeclaration>().FirstOrDefault();
return Encoding.GetEncoding(declaration.Encoding);
}
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.
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;
}
I have to create an XML file with all the elements prefixed, like this:
<ps:Request num="123" xmlns:ps="www.ladieda.com">
<ps:ClientId>5566</ps:ClientId>
<ps:Request>
When i serialize my object, c# is smart and does this:
<Request num="123" xmlns="www.ladieda.com">
<ClientId>5566</ClientId>
<Request>
That is good, because the ps: is not necessary.
But is there a way to force C# to serialize all the prefixes?
My serialize code is this (for incoming object pObject):
String XmlizedString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(pObject.GetType());
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
xs.Serialize(xmlTextWriter, pObject);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return XmlizedString;
private String UTF8ByteArrayToString(Byte[] characters)
{
UTF8Encoding encoding = new UTF8Encoding();
String constructedString = encoding.GetString(characters);
return (constructedString);
}
First of all, if the consumer of your string were processing XML, then they wouldn't care about the prefix, since it doesn't matter (to XML). Perhaps they don't understand XML, and think they're processing a string (which might need to have the string "ps:" on every element).
Second of all, you should change your code a bit:
XmlSerializer xs = new XmlSerializer(pObject.GetType());
using (MemoryStream memoryStream = new MemoryStream())
{
XmlWriterSettings settings = new XmlWriterSettings()
{
Encoding = Encoding.UTF8
};
using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
xs.Serialize(writer, pObject);
}
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
This will properly dispose of the stream and XmlWriter if an exception is thrown, stops using the deprecated XmlTextWriter class, and yet still returns a string containing XML written for UTF-8.
Finally, to control the prefix, see "How to: Qualify XML Element and XML Attribute Names":
XmlSerializerNamespaces myNamespaces = new XmlSerializerNamespaces();
myNamespaces.Add("ps", "www.ladieda.com");
XmlSerializer xs = new XmlSerializer(pObject.GetType());
using (MemoryStream memoryStream = new MemoryStream())
{
XmlWriterSettings settings = new XmlWriterSettings()
{
Encoding = Encoding.UTF8
};
using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
xs.Serialize(writer, pObject, myNamespaces);
}
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
Also check out XmlNamespaceDeclarationsAttribute. Caveat: when deserializing it will only give you namespaces defined by that element, it won't have namespaces defined in parent elements. If you don't have a consistent root type then use the XmlSerializer.Serialize() overload from #John Saunders.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlnamespacedeclarationsattribute.aspx
In another question #John Saunders suggests using this attribute in regards to controlling xmlns in WSDL: Namespace Prefixes in Wsdl (.net)
From MSDN Sample:
// C#
using System;
using System.IO;
using System.Xml.Serialization;
[XmlRoot("select")]
public class Select {
[XmlAttribute] public string xpath;
[XmlNamespaceDeclarations] public XmlSerializerNamespaces xmlns;
}
public class Test {
public static void Main(string[] args) {
Select mySelect = new Select();
mySelect.xpath = "myNS:ref/#common:y";
mySelect.xmlns = new XmlSerializerNamespaces();
mySelect.xmlns.Add("MyNS", "myNS.tempuri.org");
mySelect.xmlns.Add("common", "common.tempuri.org");
XmlSerializer ser = new XmlSerializer(typeof(Select));
ser.Serialize(Console.Out, mySelect);
}
}
// Output:
// <?xml version="1.0" encoding="IBM437"?>
// <select xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmln:xsi="http://www.w3.org/2001/XMLSchema-instance"
// xmlns:common="common.tempuri.org" xmlns:MyNS="myNS.tempuri.org" xpath="myNS:ref/#common:y" />