Why does XDeclaration ignore the encoding parameter I set - c#

The problem
I am trying to generate a XML file with the XDeclaration that should result in the following
<?xml version="1.0"?>.
What I keep getting when I run my piece of code is this
<?xml version="1.0" encoding="utf-8"?>.
So the problem is, no matter what I change in the encoding parameter of my XDeclaration it keeps adding an encoding tag to my declaration.
My question
Is what I want even possible? And if so could someone explain it to me?
What I have tried
First I tried to set OmitXmlDeclaration to true. But that removes my declaration entirely and then I can't use the XDecleration class at all..
Secondly, when I set my XDecleration to this:
new XDeclaration("1.0", string.Empty, "yes")
I get this
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
and when I set my XDecleration to this:
new XDeclaration("1.0", string.Empty, string.Empty)
I get this
<?xml version="1.0" encoding="utf-8"?>
So I know that it does respond to what I enter, but the encoding part does not.
EDIT: I also tried to set the parameters to null instead of string.Empty. This doesn't work either.
My Code
public FileResult Download()
{
var doc = new XDocument(
new XDeclaration("1.0", string.Empty, string.Empty),
new XElement("foo",
new XAttribute("hello", "world")
)
);
using (var stream = new MemoryStream())
{
var settings = new XmlWriterSettings()
{
OmitXmlDeclaration = false,
Indent = true,
};
using (var writer = XmlWriter.Create(stream, settings))
{
doc.Save(writer);
}
stream.Position = 0;
return File(stream.ToArray(), "text/xml", "HelloWorld.xml");
}
}
EDIT
De solution was given to me by #HimBromBeere. He gave an answer with the following link included.
cookcomputing.com/blog/archives/000577.html. Here you overide your encoding parameter. This worked for me.

By default XML save method will override the encoding method supplied by us via XDeclaration while saving the Xdocument. The easiest way is to create a memorystream and patch the encoding method. Refer The following code fragment
public static void PatchEncoding(string xmlPath, XDocument xmlDoc, string encoding = "windows-1251")
{
using (var stream = new MemoryStream())
using (XmlTextWriter xmlwriter = new XmlTextWriter(stream, Encoding.GetEncoding(encoding)))
{
xmlDoc.Save(xmlPath);
}
}

Related

Encoding attribute from XML file Declaration turns to lower case when the file is created by a MemoryStream

I have to generate a XML file using "ISO-8859-1" encoding from my Asp.Net Web API application but MemoryStream lowercases the encoding attribute from the generated XML definition to "iso-8859-1".
This method generates a XML file based on a object which has been created by a XSD.
public static MemoryStream GenerateXml<T>(T entity) where T : class
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
//Add an empty namespace and empty value
ns.Add("", "");
var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, Encoding.GetEncoding("ISO-8859-1"));
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(streamWriter, entity, ns);
return memoryStream;
}
Then I need to use XDocument to replace the prefix definition of XML elements (Its a prerequisite that all elements should be only named with their own tags). So I had to do this:
public MemoryStream GenerateXmlOpening<T>(T entity) where T : class
{
var xmlMemStream = XmlHelper.GenerateXml(entity);
xmlMemStream.Position = 0;
XDocument doc = XDocument.Load(xmlMemStream, LoadOptions.PreserveWhitespace);
//Removes the namespace declaration as prefix on elements
doc.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
//the memory stream retreived from 'xmlMemStream' is already with "iso-8859-1 in lowercase, so im trying to override it
doc.Declaration.Encoding = "ISO-8859-1";
MemoryStream stream = new MemoryStream();
// when i save the xdoc to the new memorystream, the encoding goes from "ISO-8859-1" to "iso-8859-1" again.
doc.Save(stream);
stream.Position = 0;
return stream;
}
This is the beginning of the returned generated XML file:
<?xml version="1.0" encoding="iso-8859-1"?>
... content
How it's supposed to be:
<?xml version="1.0" encoding="ISO-8859-1"?>
... content
Ps.* Im writing the XML using a MemoryStream because I have to write a .zip file and return a response of all generated XML files within this zip. This .Zip generator receives a list of MemoryStreams.

Avoid XML Escape Double Quote

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.

Deserialization Xml Document Error

I have a problem when I deserialize the xml into List of Objects. I searched it on the net this morning, but my problem isn't resolved.
Deserialization method
public static List<FileAction> DeSerialization()
{
XmlRootAttribute xRoot=new XmlRootAttribute();
xRoot.ElementName="ArrayOfSerializeClass";
xRoot.IsNullable=true;
XmlSerializer serializer = new XmlSerializer(typeof(List<FileAction>),xRoot);//, new XmlRootAttribute("ArrayOfSerializeClass")
using (Stream streamReader = File.OpenRead(#"C:\serialization\SerializationWithFileWatcher\Output\XmlSerialize.xml"))//FileStream fs =new FileStream(xmlPath,FileMode.Open)
{
using (XmlReader reader = XmlReader.Create(streamReader))
{
int count =0;
List<FileAction> serialList2 = (List<FileAction>)serializer.Deserialize(reader);
return (List<FileAction>)serializer.Deserialize(reader);
}
}
Calling Method
String resultPath = #"C:\serialization\SerializationWithFileWatcher\Output\XmlSerialize.xml";
if (!File.Exists(resultPath))
{
XmlSerializer xs = new XmlSerializer(typeof(List<SerializeClass>));
using (FileStream fileStream = new FileStream(#"C:\serialization\SerializationWithFileWatcher\Output\XmlSerialize.xml", FileMode.Create))
{
xs.Serialize(fileStream, serializeList);//seri
fileStream.Close();
}
Console.WriteLine("Succesfully serialized to XML");
}
else
{
//string path= #"C:\serialization\SerializationWithFileWatcher\Output\XmlSerialize.xml";
DeSerialization();
XmlSerializer xs = new XmlSerializer(typeof(List<SerializeClass>));
FileStream fs = new FileStream(#"C:\serialization\SerializationWithFileWatcher\Output\XmlSerialize.xml", FileMode.Append, FileAccess.Write);
using (XmlWriter xwr = XmlWriter.Create(fs))//TextWriter xwr = new StreamWriter
{
xs.Serialize(xwr, serializeList);//seri
//fs.Close();
}
Console.WriteLine("Succesfully serialized to XML");
}
return serializeList;
The reason why I am calling it here is that I want to add this object again to the xml file.
THe error is that here is an error in XML document (15,27).
My Xml structure
<?xml version="1.0"?>
<ArrayOfSerializeClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SerializeClass>
<creationTime>2013-11-25T09:53:25.3325289+05:30</creationTime>
<fileAction>Renamed</fileAction>
<Properties>
<FileAttributes fileName="validate json.txt">
<fileSize>307</fileSize>
<extension>.txt</extension>
<lastAccessTime>2013-11-25T09:53:25.3325289+05:30</lastAccessTime
<fullPath>C:\serialization\SerializationWithFileWatcher\SerializationWithFileWatcherProj\validate json.txt</fullPath>
</FileAttributes>
</Properties>
</SerializeClass>
</ArrayOfSerializeClass>
What I understand from the code above is that you are trying to extend the current XML, by first reading it as a FileStream and then using an XmlWriter to add some more content to it.
If my understanding is correct, then you are trying to write to the end of an existing XML file, which is not allowed since any XML document can have only one root node. In your case, that root node is ArrayOfSerializeClass.
So, in order to successfully achieve your task, you must append your XML within the root node.
Update:
Possible solution here: how to append a xml file in c#?

Add XDeclaration to XDocument after it's constructed

I have an XmlSerializer which I use to Serialize an object to an XDocument.
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
xmlSerializer.Serialize(writer, object);
}
After this is done, I want to add a XDeclaration:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
I construct this XDeclaration as described below:
var decl = new XDeclaration("1.0", "UTF-8", "no");
However, when I try to add this XDeclartion to my XDocument, I get the following error:
System.ArgumentException : Non white space characters cannot be added to content.
I searched Google for some time but all I've found is adding the XDeclaration to the constructor of the XDocument which in my case (when filling it with a XmlWriter) is not acceptable.
Use property XDocument.Declaration
EDIT:
Sample code:
var xmlSerializer = new XmlSerializer(typeof(int));
var doc = new XDocument();
var decl = new XDeclaration("1.0", "utf-8", "no");
doc.Declaration = decl;
using (var writer = doc.CreateWriter())
{
xmlSerializer.Serialize(writer, 1);
}
doc.Save(File.Create("x.xml"));
This code produced following output:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<int>1</int>

How to get Xml as string from XDocument?

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);

Categories