Person person = GetPerson();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
XmlSerializer serializer = new XmlSerializer(typeof(Person));
string personText = string.Empty;
using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, new XmlWriterSettings() { Encoding = Encoding.UTF8 }))
{
serializer.Serialize(xmlWriter, person, ns);
xmlWriter.Flush();
personText = Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
string path = #"D:\person.xml";
// Write method 1:
File.WriteAllText(path, personText);
// Write method 2:
using (StreamWriter streamWriter = new StreamWriter(path, false , Encoding.UTF8))
{
streamWriter.Write(personText);
}
// Read the xml
using (FileStream fileStream = new FileStream(path, FileMode.Open))
{
return XDocument.Load(XmlReader.Create(fileStream));
}
When I read the xml after writing using method 2, I get this Data at the root level is invalid. Line 1, position 1. But it works fine using method 1.
What is causing this? Any pointers appreciated.
The problem is that both the StreamWriter and the XmlWriter are adding a byte-order-mark.
Options:
String the BOM from personText to start with
Pass new UTF8Encoding(false) instead of Encoding.UTF8 for the StreamWriter
Pass new UTF8Encoding(false) instead of Encoding.UTF8 for the XmlWriter
Avoid converting to text and back again in the first place: you've got the binary data in the MemoryStream, why not just dump that to disk?
Related
I have the simplest code in the world,
using (XmlWriter writer = XmlWriter.Create(stringWriter))
{
writer.WriteStartDocument();
writer.WriteStartElement("Board");
writer.WriteAttributeString("Rows", mRows.ToString());
writer.WriteAttributeString("Columns", mColumns.ToString());
writer.WriteEndElement();
writer.WriteEndDocument();
}
TextWriter writer1 = new StreamWriter(path);
writer1.Write(stringWriter.toString());
writer1.Close();
Then i write it to a txt file that looks like this:
<?xml version="1.0" encoding="utf-16"?>
<Board Rows="30" Columns="50">
</Board>
Then i do the following :
FileStream str = new FileStream(s.FileName, FileMode.Open);
using(XmlReader reader = XmlReader.Create(stream))
{
reader.Read();
}
And it throws an exception :
"There is no Unicode byte order mark. Cannot switch to Unicode."
I googled the exception and found several workarounds, but i don't understand why i need a work around, i just want to read the xml i wrote.
Can some one please explain what exactly the problem is ?
Should i write something differently in the xml ?
What is the simplest solution to this ?
You're probably not writing to a unicode file which File.WriteAllText or a vanilla FileStream does not do.
Instead use File.OpenWrite or FileStream combined with the StreamWriter(Stream steam, Encoding encoding) constructor to specify unicode.
Sample:
var path = #"C:\Dev\sample.xml";
string xml;
var mRows = 30;
var mColumns = 50;
var options = new XmlWriterSettings { Indent = true };
using (var stringWriter = new StringWriter())
{
using (var writer = XmlWriter.Create(stringWriter, options))
{
writer.WriteStartDocument();
writer.WriteStartElement("Board");
writer.WriteAttributeString("Rows", mRows.ToString());
writer.WriteAttributeString("Columns", mColumns.ToString());
writer.WriteEndElement();
writer.WriteEndDocument();
}
xml = stringWriter.ToString();
}
if(File.Exists(path))
File.Delete(path);
using(var stream = File.OpenWrite(path))
using(var writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(xml);
}
Console.Write(xml);
using(var stream = File.OpenRead(path))
using(var reader = XmlReader.Create(stream))
{
reader.Read();
}
File.Delete(path);
This must be very simple but I can't find it by searching.
I have the following code to serialize an object to a file and back. But now I want to serialize to a byte[] and back.
XmlSerializer serializer = new XmlSerializer(typeof(Class1));
using (TextWriter textWriter = new StreamWriter(path))
serializer.Serialize(textWriter, class1);
using (TextReader textReader = new StreamReader(path))
class1b = (Class1)serializer.Deserialize(textReader);
I tried using a MemoryStream:
byte[] buffer = new byte[1000];
using (TextWriter textWriter = new MemoryStream(buffer))
...
but I get an error. So how should I do it?
You should send the stream to the StreamWriter instead of trying to assign the Stream to a TextWriter.
using (var stream = new MemoryStream(buffer))
{
using (TextWriter textWriter = new StreamWriter(stream))
{ ... }
}
I have an XmlTextWriter that gets written to file using an XmlSerializer that looks like the following:
using (XmlTextWriter writer = new XmlTextWriter(path, null))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 3;
MyFileObj.ourSerializer.Serialize(writer, xmlFile, ourXmlNamespaces);
}
where "ourSerializer" is just a reference to an System.Xml.Serialization.XmlSerializer object. However, I have an instance where this XML must be encrypted to disk so that the end user cannot read its contents, and I am unsure of the proper way to go about it using the existing code since there are many places where this code is called and does not need to be encrypted. Can anyone shed some insight into this for me?
An alternative way would be to use a CryptoStream, like this:
using (var fs = new FileStream(path, System.IO.FileMode.Create))
{
using (var cs = new CryptoStream(fs, _Provider.CreateEncryptor(), CryptoStreamMode.Write))
{
using (var writer = XmlWriter.Create(cs))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 3;
MyFileObj.ourSerializer.Serialize(writer, xmlFile, ourXmlNamespaces);
}
}
}
Where _Provider is an AesCryptoServiceProvider properly initialized.
Here is how I ended up solving the issue:
MemoryStream ms = new MemoryStream();
XmlSerializer ourSerializer.Serialize(ms, xmlFile, ourXmlNamespaces);
ms.Position = 0;
//Encrypt the memorystream
using (TextReader reader = new StreamReader(ms, Encoding.ASCII))
using (StreamWriter writer = new StreamWriter(path))
{
string towrite = Encrypt(reader.ReadToEnd());
writer.Write(towrite);
}
Basically serialized the XML to a MemoryStream, read the text back out into a TextReader, encrypted the TextReader contents and then saved the resulting encrypted string to a file.
I am using the following code to create an xml document -
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
new XmlSerializer(typeof(docket)).Serialize(Console.Out, i, ns);
this works great in creating the xml file with no namespace attributes. i would like to also have no encoding attribute in the root element, but I cannot find a way to do it. Does anyone have any idea if this can be done?
Thanks
Old answer removed and update with new solution:
Assuming that it's ok to remove the xml declaration completly, because it makes not much sense without the encoding attribute:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(Console.Out, new XmlWriterSettings { OmitXmlDeclaration = true}))
{
new XmlSerializer(typeof (SomeType)).Serialize(writer, new SomeType(), ns);
}
To remove encoding from XML header pass TextWriter with null encoding to XmlSerializer:
MemoryStream ms = new MemoryStream();
XmlTextWriter w = new XmlTextWriter(ms, null);
s.Serialize(w, vs);
Explanation
XmlTextWriter uses encoding from TextWriter passed in constructor:
// XmlTextWriter constructor
public XmlTextWriter(TextWriter w) : this()
{
this.textWriter = w;
this.encoding = w.Encoding;
..
It uses this encoding when generating XML:
// Snippet from XmlTextWriter.StartDocument
if (this.encoding != null)
{
builder.Append(" encoding=");
...
string withEncoding;
using (System.IO.MemoryStream memory = new System.IO.MemoryStream()) {
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(memory)) {
serializer.Serialize(writer, obj, null);
using (System.IO.StreamReader reader = new System.IO.StreamReader(memory)) {
memory.Position = 0;
withEncoding= reader.ReadToEnd();
}
}
}
string withOutEncoding= withEncoding.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "");
Credit to this blog for helping me with my code
http://blog.dotnetclr.com/archive/2008/01/29/removing-declaration-and-namespaces-from-xml-serialization.aspx
here's my solution, same idea, but in VB.NET and a little clearer in my opinion.
Dim sw As StreamWriter = New, StreamWriter(req.GetRequestStream,System.Text.Encoding.ASCII)
Dim xSerializer As XmlSerializer = New XmlSerializer(GetType(T))
Dim nmsp As XmlSerializerNamespaces = New XmlSerializerNamespaces()
nmsp.Add("", "")
Dim xWriterSettings As XmlWriterSettings = New XmlWriterSettings()
xWriterSettings.OmitXmlDeclaration = True
Dim xmlWriter As XmlWriter = xmlWriter.Create(sw, xWriterSettings)
xSerializer.Serialize(xmlWriter, someObjectT, nmsp)
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" />