I'm currently trying to serialize a class into XML to be posted to php web service.
Whenever I did the normal serialization using XMLSerializer, XML declaration is always appear in the first line of the XML document (similar as to <?xml ....?>). I tested the XML and unable to get it working because the endpoint does not accept XML declaration and I can't do anything about it.
I'm unfamiliar with XML Serialization in C# to be honest.
Therefore, I used XMLWriter to do this as below :-
private string SerializeClassToString(GetRiskReport value)
{
var emptyNS = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var ser = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (var stream = new StringWriter())
{
using (var writer = XmlWriter.Create(stream, settings))
{
ser.Serialize(writer, value, emptyNS);
return stream.ToString();
}
}
}
Result for the Namespace is
<GetRiskReport FCRA=\"false\" ReturnResultsOnly=\"false\" Monitoring=\"false\">
... and I'm able to omit the XML Declaration, however I'm being introduced with 2 new problem.
I got \r\n for new line and I have escaped double quote such as ReturnResultsOnly=\"false\" Monitoring=\"false\" which is also unable processed by the endpoint.
I would like to ask is that does anyone can give me an idea on how to change the XmlWriterSetting to omit XML Declaration, avoid \r\n and also avoid escaped double quotes \"
Thanks for your advice in advance.
Simon
Try with following settings
settings.NewLineHandling = NewLineHandling.None;
settings.CheckCharacters = false;
private void SerializeClassToString(GetRiskReport value)
{
var emptyNS = new XmlSerializerNamespaces(new[]{XmlQualifiedName.Empty});
var ser = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
string path = 'your_file_path_here'
if (File.Exists(path)) File.Delete(path);
FileStream stream = File.Create(path);
using (var writer = XmlWriter.Create(stream, settings))
{
ser.Serialize(writer, value, emptyNS);
return;
}
}
There was no way to avoid ms bug or thier intensional specification about xmlserializing.It's easier and faster to use filestream object.
Related
I am writing to the file XML serialization of the object, generated by validator.MatchPossiblyValid(string input)method. First call, serializes and write to the file. However, the second call fails with an exception: System.InvalidOperationException: 'Token StartElement in state EndRootElement 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. '
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var serializer = new XmlSerializer(typeof(PDPCustomerInfoInvalid));
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
//settings.Indent = true;
using (var stream = new System.IO.StreamWriter(args[1], true))
{
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, validator.MatchPossiblyValid("STRING FOR PARSING"), emptyNS);
stream.Write(Environment.NewLine);
stream.Flush();
//Line below throws the exception
serializer.Serialize(writer, validator.MatchPossiblyValid("STRING FOR PARSING"), emptyNS);
stream.Write(Environment.NewLine);
stream.Flush();
}
}
You are trying to use a single XmlWriter to create an XML file with multiple root elements. However, the XML standard requires exactly one root element per XML document. Your XmlWriter is throwing the exception to indicate that the XML being created is invalid. (MCVE here.)
If you really need to concatenate two XML documents into a single file, you could use separate XmlWriters created with XmlWriterSettings.CloseOutput set to false:
using (var stream = new System.IO.StreamWriter(args[1], true))
{
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
//settings.Indent = true;
settings.CloseOutput = false;
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, validator.MatchPossiblyValid("STRING FOR PARSING"), emptyNS);
}
stream.Write(Environment.NewLine);
stream.Flush();
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, validator.MatchPossiblyValid("STRING FOR PARSING"), emptyNS);
}
//Line below throws the exception
stream.Write(Environment.NewLine);
stream.Flush();
}
Sample fiddle.
Or, better yet, don't do this at all, since an "XML Document" with multiple roots is, as stated above, not valid. Instead, serialize both objects inside some container element.
I'm building an MVC5 application which pulls records from a database and allows a user to perform some basic data cleansing edits.
Once the data has been cleansed it needs to be exported as XML, run through a validator and then uploaded to a third party portal.
I'm using Service Stack, and I've found it fairly quick and straightforward in the past, particularly when outputting to CSV.
The one issue I'm having is with the XML serialzer. I'm not sure how to make it generate well formed XML.
The file that i'm getting simply dumps it on one line, which won't validate because it isn't well formed.
below is an extract from my controller action:
Response.Clear();
Response.ContentType = "text/xml";
Response.AddHeader("Content-Disposition", "attachment; filename="myFile.xml"");
XmlSerializer.SerializeToStream(viewModel, Response.OutputStream);
Response.End();
UPDATE: Thanks for the useful comments, as explained I'm not talking about pretty printing, the issue is I need to run the file through a validator before uploading it to a third party. The error message the validator is throwing is Error:0000, XML not well-formed. Cannot have more than one tag on one line.
Firstly, be aware that most white space (including new lines) in XML is insignificant -- it has no meaning, and is only for beautification. The lack of new lines doesn't make the XML ill-formed. See White Space in XML Documents or https://www.w3.org/TR/REC-xml/#sec-white-space. Thus in theory it shouldn't matter whether ServiceStack's XmlSerializer is putting all of your XML on a single line.
That being said, if for whatever reason you must cosmetically break your XML up into multiple lines, you'll need to do a little work. From the source code we can see that XmlSerializer uses DataContractSerializer with a hardcoded static XmlWriterSettings that does not allow for setting XmlWriterSettings.Indent = true. However, since this class is just a very thin wrapper on Microsoft's data contract serializer, you can substitute your own code:
public static class DataContractSerializerHelper
{
private static readonly XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Indent = true, IndentChars = " " };
public static string SerializeToString<T>(T from)
{
try
{
using (var ms = new MemoryStream())
using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
{
var serializer = new DataContractSerializer(from.GetType());
serializer.WriteObject(xw, from);
xw.Flush();
ms.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(ms);
return reader.ReadToEnd();
}
}
catch (Exception ex)
{
throw new SerializationException(string.Format("Error serializing \"{0}\"", from), ex);
}
}
public static void SerializeToWriter<T>(T value, TextWriter writer)
{
try
{
using (var xw = XmlWriter.Create(writer, xmlWriterSettings))
{
var serializer = new DataContractSerializer(value.GetType());
serializer.WriteObject(xw, value);
}
}
catch (Exception ex)
{
throw new SerializationException(string.Format("Error serializing \"{0}\"", value), ex);
}
}
public static void SerializeToStream(object obj, Stream stream)
{
if (obj == null)
return;
using (var xw = XmlWriter.Create(stream, xmlWriterSettings))
{
var serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(xw, obj);
}
}
}
And then do:
DataContractSerializerHelper.SerializeToStream(viewModel, Response.OutputStream);
I would like to use JsonFx to convert XML to/from custom types and LINQ queries. Can anyone please provide an example to de-serialisation and serialisation back again?
Here's an example of the XML I'm working with.
XML pasted here: http://pastebin.com/wURiaJM2
JsonFx Supports several strategies of binding json to .net objects including dynamic objects. https://github.com/jsonfx/jsonfx
Kind regards
Si
PS I did try pasting the xml document into StackOverflow but it removed a lot of the documents quotes and XML declaration.
Here's a method that I have used. It may require some tweaking:
public static string SerializeObject<T>(T item, string rootName, Encoding encoding)
{
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.OmitXmlDeclaration = true;
writerSettings.Indent = true;
writerSettings.NewLineHandling = NewLineHandling.Entitize;
writerSettings.IndentChars = " ";
writerSettings.Encoding = encoding;
StringWriter stringWriter = new StringWriter();
using (XmlWriter xml = XmlWriter.Create(stringWriter, writerSettings))
{
XmlAttributeOverrides aor = null;
if (rootName != null)
{
XmlAttributes att = new XmlAttributes();
att.XmlRoot = new XmlRootAttribute(rootName);
aor = new XmlAttributeOverrides();
aor.Add(typeof(T), att);
}
XmlSerializer xs = new XmlSerializer(typeof(T), aor);
XmlSerializerNamespaces xNs = new XmlSerializerNamespaces();
xNs.Add("", "");
xs.Serialize(xml, item, xNs);
}
return stringWriter.ToString();
}
And for Deserialization:
public static T DeserializeObject<T>(string xml)
{
using (StringReader rdr = new StringReader(xml))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(rdr);
}
}
And call it like this:
string xmlString = Serialization.SerializeObject(instance, "Root", Encoding.UTF8);
ObjectType obj = Serialization.DeserializeObject<ObjectType>(xmlString);
Hope this helps. The rootName parameter in the Serialize method lets you customize the value of the root node in the resulting xml string. Also, your classes must be decorated with the proper Xml attributes which will control how an entity is serialized.
This post explains how to create an XSD and a Classes from an XML file and then covers serialisation and de-serialisation.
http://geekswithblogs.net/CWeeks/archive/2008/03/11/120465.aspx
Using this technique with the XSD.exe to create an XSD and then classes in a CS file I was able to serialisation and then de-serialisation back again.
However the serialisation process does not create an exact representation of the source XML, so there's still some post work to be done there.
I am new to LINQ to XML. After you have built XDocument, how do you get the OuterXml of it like you did with XmlDocument?
You only need to use the overridden ToString() method of the object:
XDocument xmlDoc ...
string xml = xmlDoc.ToString();
This works with all XObjects, like XElement, etc.
I don't know when this changed, but today (July 2017) when trying the answers out, I got
"System.Xml.XmlDocument"
Instead of ToString(), you can use the originally intended way accessing the XmlDocument content: writing the xml doc to a stream.
XmlDocument xml = ...;
string result;
using (StringWriter writer = new StringWriter())
{
xml.Save(writer);
result = writer.ToString();
}
Several responses give a slightly incorrect answer.
XDocument.ToString() omits the XML declaration (and, according to #Alex Gordon, may return invalid XML if it contains encoded unusual characters like &).
Saving XDocument to StringWriter will cause .NET to emit encoding="utf-16", which you most likely don't want (if you save XML as a string, it's probably because you want to later save it as a file, and de facto standard for saving files is UTF-8 - .NET saves text files as UTF-8 unless specified otherwise).
#Wolfgang Grinfeld's answer is heading in the right direction, but it's unnecessarily complex.
Use the following:
var memory = new MemoryStream();
xDocument.Save(memory);
string xmlText = Encoding.UTF8.GetString(memory.ToArray());
This will return XML text with UTF-8 declaration.
Doing XDocument.ToString() may not get you the full XML.
In order to get the XML declaration at the start of the XML document as a string, use the XDocument.Save() method:
var ms = new MemoryStream();
using (var xw = XmlWriter.Create(new StreamWriter(ms, Encoding.GetEncoding("ISO-8859-1"))))
new XDocument(new XElement("Root", new XElement("Leaf", "data"))).Save(xw);
var myXml = Encoding.GetEncoding("ISO-8859-1").GetString(ms.ToArray());
Use ToString() to convert XDocument into a string:
string result = string.Empty;
XElement root = new XElement("xml",
new XElement("MsgType", "<![CDATA[" + "text" + "]]>"),
new XElement("Content", "<![CDATA[" + "Hi, this is Wilson Wu Testing for you! You can ask any question but no answer can be replied...." + "]]>"),
new XElement("FuncFlag", 0)
);
result = root.ToString();
While #wolfgang-grinfeld's answer is technically correct (as it also produces the XML declaration, as opposed to just using .ToString() method), the code generated UTF-8 byte order mark (BOM), which for some reason XDocument.Parse(string) method cannot process and throws Data at the root level is invalid. Line 1, position 1. error.
So here is a another solution without the BOM:
var utf8Encoding =
new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
using (var memory = new MemoryStream())
using (var writer = XmlWriter.Create(memory, new XmlWriterSettings
{
OmitXmlDeclaration = false,
Encoding = utf8Encoding
}))
{
CompanyDataXml.Save(writer);
writer.Flush();
return utf8Encoding.GetString(memory.ToArray());
}
I found this example in the Microsoft .NET 6 documentation for XDocument.Save method. I think it answers the original question (what is the XDocument equivalent for XmlDocument.OuterXml), and also addresses the concerns that others have pointed out already. By using the XmlWritingSettings you can predictably control the string output.
https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.save
StringBuilder sb = new StringBuilder();
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
using (XmlWriter xw = XmlWriter.Create(sb, xws)) {
XDocument doc = new XDocument(
new XElement("Child",
new XElement("GrandChild", "some content")
)
);
doc.Save(xw);
}
Console.WriteLine(sb.ToString());
Looking at these answers, I see a lot of unnecessary complexity and inefficiency in pursuit of generating the XML declaration automatically. But since the declaration is so simple, there isn't much value in generating it. Just KISS (keep it simple, stupid):
// Extension method
public static string ToStringWithDeclaration(this XDocument doc, string declaration = null)
{
declaration ??= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
return declaration + doc.ToString();
}
// Usage
string xmlString = doc.ToStringWithDeclaration();
// Or
string xmlString = doc.ToStringWithDeclaration("...");
Using XmlWriter instead of ToString() can give you more control over how the output is formatted (such as if you want indentation), and it can write to other targets besides string.
The reason to target a memory stream is performance. It lets you skip the step of storing the XML in a string (since you know the data must end up in a different encoding eventually, whereas string is always UTF-16 in C#). For instance, for an HTTP request:
// Extension method
public static ByteArrayContent ToByteArrayContent(
this XDocument doc, XmlWriterSettings xmlWriterSettings = null)
{
xmlWriterSettings ??= new XmlWriterSettings();
using (var stream = new MemoryStream())
{
using (var writer = XmlWriter.Create(stream, xmlWriterSettings))
{
doc.Save(writer);
}
var content = new ByteArrayContent(stream.GetBuffer(), 0, (int)stream.Length);
content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
return content;
}
}
// Usage (XDocument -> UTF-8 bytes)
var content = doc.ToByteArrayContent();
var response = await httpClient.PostAsync("/someurl", content);
// Alternative (XDocument -> string -> UTF-8 bytes)
var content = new StringContent(doc.ToStringWithDeclaration(), Encoding.UTF8, "text/xml");
var response = await httpClient.PostAsync("/someurl", content);
I'm serializing an object in a C# VS2003 / .Net 1.1 application. I need it serialized without the processing instruction, however. The XmlSerializer class puts out something like this:
<?xml version="1.0" encoding="utf-16" ?>
<MyObject>
<Property1>Data</Property1>
<Property2>More Data</Property2>
</MyObject>
Is there any way to get something like the following, without processing the resulting text to remove the tag?
<MyObject>
<Property1>Data</Property1>
<Property2>More Data</Property2>
</MyObject>
For those that are curious, my code looks like this...
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
StringBuilder builder = new StringBuilder();
using ( TextWriter stringWriter = new StringWriter(builder) )
{
serializer.Serialize(stringWriter, comments);
return builder.ToString();
}
I made a small correction
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using ( XmlWriter stringWriter = XmlWriter.Create(builder, settings) )
{
serializer.Serialize(stringWriter, comments);
return builder.ToString();
}
In 2.0, you would use XmLWriterSettings.OmitXmlDeclaration, and serialize to an XmlWriter - however I don't think this exists in 1.1; so not entirely useful - but just one more "consider upgrading" thing... and yes, I realise it isn't always possible.
The following link will take you to a post where someone has a method of supressing the processing instruction by using an XmlWriter and getting into an 'Element' state rather than a 'Start' state. This causes the processing instruction to not be written.
Suppress Processing Instruction
If you pass an XmlWriter to the serializer, it will only emit a processing
instruction if the XmlWriter's state is 'Start' (i.e., has not had anything
written to it yet).
// Assume we have a type named 'MyType' and a variable of this type named
'myObject'
System.Text.StringBuilder output = new System.Text.StringBuilder();
System.IO.StringWriter internalWriter = new System.IO.StringWriter(output);
System.Xml.XmlWriter writer = new System.Xml.XmlTextWriter(internalWriter);
System.Xml.Serialization.XmlSerializer serializer = new
System.Xml.Serialization.XmlSerializer(typeof(MyType));
writer.WriteStartElement("MyContainingElement");
serializer.Serialize(writer, myObject);
writer.WriteEndElement();
In this case, the writer will be in a state of 'Element' (inside an element)
so no processing instruction will be written. One you finish writing the
XML, you can extract the text from the underlying stream and process it to
your heart's content.
What about omitting namespaces ?
instead of using
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
ex:
<message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
If by "processing instruction" you mean the xml declaration, then you can avoid this by setting the OmitXmlDeclaration property of XmlWriterSettings. You'll need to serialize using an XmlWriter, to accomplish this.
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using ( XmlWriter stringWriter = new StringWriter(builder, settings) )
{
serializer.Serialize(stringWriter, comments);
return builder.ToString();
}
But ah, this doesn't answer your question for 1.1. Well, for reference to others.
This works in .NET 1.1. (But you should still consider upgrading)
XmlSerializer s1= new XmlSerializer(typeof(MyClass));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add( "", "" );
MyClass c= new MyClass();
c.PropertyFromDerivedClass= "Hallo";
sw = new System.IO.StringWriter();
s1.Serialize(new XTWND(sw), c, ns);
....
/// XmlTextWriterFormattedNoDeclaration
/// helper class : eliminates the XML Documentation at the
/// start of a XML doc.
/// XTWFND = XmlTextWriterFormattedNoDeclaration
public class XTWFND : System.Xml.XmlTextWriter
{
public XTWFND(System.IO.TextWriter w) : base(w) { Formatting = System.Xml.Formatting.Indented; }
public override void WriteStartDocument() { }
}