Given this generic serialization code:
public virtual string Serialize(System.Text.Encoding encoding)
{
System.IO.StreamReader streamReader = null;
System.IO.MemoryStream memoryStream = null;
memoryStream = new System.IO.MemoryStream();
System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings();
xmlWriterSettings.Encoding = encoding;
System.Xml.XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings);
Serializer.Serialize(xmlWriter, this);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
streamReader = new System.IO.StreamReader(memoryStream);
return streamReader.ReadToEnd();
}
and this object (gen'd from xsd2code):
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "Com.Foo.Request")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "Com.Foo.Request", IsNullable = false)]
public partial class REQUEST_GROUP
{
[EditorBrowsable(EditorBrowsableState.Never)]
private List<REQUESTING_PARTY> rEQUESTING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private RECEIVING_PARTY rECEIVING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private SUBMITTING_PARTY sUBMITTING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private REQUEST rEQUESTField;
[EditorBrowsable(EditorBrowsableState.Never)]
private string iDField;
public REQUEST_GROUP()
{
this.rEQUESTField = new REQUEST();
this.sUBMITTING_PARTYField = new SUBMITTING_PARTY();
this.rECEIVING_PARTYField = new RECEIVING_PARTY();
this.rEQUESTING_PARTYField = new List<REQUESTING_PARTY>();
this.IDField = "2.1";
}
}
Output from the Serialize with an encode of utf-8:
<?xml version="1.0" encoding="utf-8"?>
<REQUEST_GROUP xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="2.1" xmlns="Com.Foo.Request">
<RECEIVING_PARTY />
<SUBMITTING_PARTY />
<REQUEST LoginAccountIdentifier="xxx" LoginAccountPassword="yyy" _RecordIdentifier="" _JobIdentifier="">
<REQUESTDATA>
<PROPERTY_INFORMATION_REQUEST _SpecialInstructionsDescription="" _ActionType="Submit">
<_DATA_PRODUCT _ShortSubjectReport="Y" />
<_PROPERTY_CRITERIA _City="Sunshine City" _StreetAddress2="" _StreetAddress="123 Main Street" _State="CA" _PostalCode="12345">
<PARSED_STREET_ADDRESS />
</_PROPERTY_CRITERIA>
<_SEARCH_CRITERIA />
<_RESPONSE_CRITERIA />
</PROPERTY_INFORMATION_REQUEST>
</REQUESTDATA>
</REQUEST>
</REQUEST_GROUP>
EDIT
Question 1: How do I decorate the class in such a fashion, or manipulate the serializer to get rid of all the namespaces in the REQUEST_GROUP node during processing, NOT post-processing with xslt or regex.
Question 2: Bonus point if you could add the doc type too.
Thank you.
You can remove the namespaces like this:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
ns.Add(string.Empty, "Com.Foo.Request");
Serializer.Serialize(xmlWriter, this, ns);
As for adding the doctype, I know it's possible to make a custom XmlWriter and just override WriteStartDocument with a method that makes a call to WriteDocType, but I kind of hope someone else knows an easier way than that.
EDIT: Incidentally, I strongly recommend using using:
using(System.Xml.XmlWriter xmlWriter = XmlWriter.Create(etc.))
{
// use it here.
}
It automatically handles tidying up of the streams by calling the Dispose method when the block ends.
If you just want to remove the namespace aliases, then as already shown you can use XmlSerializerNamespaces to force XmlSerializer to use the namespace explicitly (i.e. xmlns="blah") on each element, rather than declaring an alias and using the alias instead.
However, regardless of what you do with the aliases, the fundamental name of that element is REQUEST_GROUP in the Com.Foo.Request namespace. You can't remove the namespace completely without that representing a breaking change to the underlying data - i.e. somebody somewhere is going to get an exception (due to getting data it didn't expect - specifically REQUEST_GROUP in the root namespace). In C# terms, it is the difference between System.String and My.Custom.String - sure, they are both called String, but that is just their local name.
If you want to remove all traces of the namespace, then a pragmatic option would be to edit away the Namespace=... entries from [XmlRoot(...)] and [XmlType(...)] (plus anywhere else that isn't shown in the example).
If the types are outside of your control, you can also do this at runtime using XmlAttributeOverrides - but a caveat: if you create an XmlSerializer using XmlAttributeOverrides you must cache and re-use it - otherwise your AppDomain will leak (it creates assemblies on the fly per serializer in this mode, and assemblies cannot be unloaded).
Related
I'm working on a C# program and I'm trying to serialise XML.
I have the following tag:
using System.Xml.Serialization;
...
[XmlElement("MV")]
public MultiVerse MultiVerse { get; set; }
When I don't fill in this value, the tag <MV> is not present, but I would like to get a tag <MV/> in that case:
Currently I have <HM><ID>Some_ID</ID></HM>.
I'd like to have <HM><ID>Some_ID</ID><MV/></HM>.
I already tried preceeding the line with [Required] but that didn't work, and I think that filling in the IsNullable attribute is the good approach.
Edit1, after some investigation on the internet
On the internet, there are quite some advises on modifying the XmlWriter but in my project, the whole serialisation is done as follows:
public override string ToString()
{
...
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
var serializer = new XmlSerializer(base.GetType());
serializer.Serialize(writer, this, ns);
return stream.ToString();
}
...
}
As you can see, this is so general that I prefer not to do any modifications in here, hence I'm looking for a way to customise the [XmlElement] directive.
Edit2: XmlWriter settings:
The XmlWriter settings look as follows:
// Remove Declaration
var settings = new XmlWriterSettings
{
Indent = false,
OmitXmlDeclaration = true,
NewLineHandling = NewLineHandling.None,
NewLineOnAttributes = false,
};
Does anybody have an idea?
Thanks in advance
There is https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlelementattribute.isnullable?view=net-6.0 so e.g.
[XmlElement("MV", IsNullable=true)]
public MultiVerse MultiVerse { get; set; }
would give you, for a null value, a serialization as <MV xsi:nil="true" /> (or possibly <MV xsi:nil="true"></MV> as ensuring the short tag notation is not something the standard writers give you control over but my experience is that .NET usually uses it for empty elements so you might be lucky that your wanted serialization format is the default one .NET outputs).
This is the way I'm currently solving my issue (don't laugh):
public override string ToString()
{
string temp = base.ToString();
temp = temp.Replace(" p2:nil=\"true\"", "");
temp = temp.Replace(" xmlns:p2=\"http://www.w3.org/2001/XMLSchema-instance\"", "");
temp = temp.Replace("MV />", "MV/>");
return temp;
}
This is hidious! Does anybody have a better solution?
Thanks
I'm currently working with generating XML files used to configure a server.
I've got a class generated with xsd which contains a property of the type System.Xml.XmlElement.
public class GeneratedClass
{
...
private System.Xml.XmlElement informationField;
[System.Xml.Serialization.XmlArrayItemAttribute("Information", IsNullable=false)]
public System.Xml.XmlElement Information {
get {
return this.informationField;
}
set {
this.informationField = value;
}
}
...
}
I'm having troubles "injecting" a custom object into this Information property.
public class MyExampleObject
{
public string Name { get; set; }
public string Id { get; set;
}
The program deserializes a xml file of the type GeneratedClass and after this I want to add MyExampleObject to the Informations property.
The way I currently do this is with this method:
XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild())
{
XmlSerializer serializer = new XmlSerializer(typeof(MyExampleObject));
serializer.Serialize(writer, MyObject);
}
this.Information = doc.DocumentElement;
After this I serialize the whole object to file, but when I do this I get unwanted xml namespace attributes.
<Information xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="">
I've found other posts with similar problems, but the suggested solutions, leaves me with <Information xmlns=""> which is still not a complete solution.
I get the feeling that there might be some other way to do this, but I'm not sure how.
Any suggestions?
I know that this post is very old :) But perhaps might come in handy for others seeking answer to this
Update Sorry I was a bit quick reading your question and focus on my own problem which was also regarding namespaces in some sense, but more how to remove one. I see that you use XmlSerializer and unfortunately I don't know how to manipulate namespaces added as attribute on class to serialize. Perhaps look at XmlRootAttribute that you can pass to XmlSerializer.ctor and then you have XmlSerializerNamespaces which is passed during serialization and provide mean to manipulate namespaces.
I recently stumbled upon this as well and found a solution.
var xmlDocument = new XmlDocument();
var xmlElement = xmlDocument.CreateElement(Prefix, "Document", Ns);
xmlElement.InnerXml = string.Empty;
var navigator = xmlElement.CreateNavigator();
navigator.AppendChildElement(Prefix, "InfRspnSD1", Ns, null);
navigator.MoveToFirstChild();
navigator.AppendChildElement(Prefix, "InvstgtnId", Ns, "123456789");
// At this point xmlElement contains XML and can be assigned
// to the generated class
// For pretty print only
using var sw = new StringWriter();
using var writer = System.Xml.XmlWriter.Create(sw, Settings);
xmlElement.WriteTo(writer);
writer.Flush();
return sw.ToString();
(Code on GitHub)
What I've noticed is that the namespace is included twice, one in the root node and then once again immediate descendant (first child).
# Generate XML using XNavigator
<?xml version="1.0" encoding="utf-16"?>
<supl:Document xmlns:supl="urn:iso:std:iso:20022:tech:xsd:supl.027.001.01">
<supl:InfRspnSD1 xmlns:supl="urn:iso:std:iso:20022:tech:xsd:supl.027.001.01">
<supl:InvstgtnId>123456789</supl:InvstgtnId>
</supl:InfRspnSD1>
</supl:Document>
--------------------
I'm working on a legacy application built in .NET 1.1.
Part of the functionality is to take JPG and other attachments, and serialize them into a base64 string in an XML file.
Right now the implementation just uses the framework XmlSerializer and serializes the object containing the attachments, which in turn are also just stored as object.
XmlSerializer serializer = new XmlSerializer(typeof(OmdCds));
TextWriter tw = new StreamWriter(Path.Combine(m_ExportLocation, m_XMLFileName + ".xml"));
xmlTextWriter = new XmlTextWriter(tw);
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("cdsd", "cds_dt");
ns.Add("", "cds");
ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlTextWriter.Formatting = Formatting.Indented;
xmlTextWriter.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"");
serializer.Serialize(xmlTextWriter, m_Record, ns);
xmlTextWriter.Close();
tw.Close();
Nested way down in the m_Record object is the following object:
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="cds_dt")]
public class content
{
private object itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Media", typeof(byte[]), DataType="base64Binary")]
[System.Xml.Serialization.XmlElementAttribute("TextContent", typeof(string))]
public object Item
{
get
{
return this.itemField;
}
set
{
this.itemField = value;
}
}
}
This is all extremely slow for large attachments, so I'm trying to figure out if this is the fastest way to write this to XML.
I'm also wondering if this compresses at the same time, or if I need to compress this base64 output separately?
Please keep in mind this in in .NET 1.1, and it is not an option to change the framework version. I'd also like to work within the confines of what the framework has to offer, unless I can get an incredible difference in speed from something 3rd party.
Given this generic serialization code:
public virtual string Serialize(System.Text.Encoding encoding)
{
System.IO.StreamReader streamReader = null;
System.IO.MemoryStream memoryStream = null;
memoryStream = new System.IO.MemoryStream();
System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings();
xmlWriterSettings.Encoding = encoding;
System.Xml.XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings);
Serializer.Serialize(xmlWriter, this);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
streamReader = new System.IO.StreamReader(memoryStream);
return streamReader.ReadToEnd();
}
and this object (gen'd from xsd2code):
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "Com.Foo.Request")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "Com.Foo.Request", IsNullable = false)]
public partial class REQUEST_GROUP
{
[EditorBrowsable(EditorBrowsableState.Never)]
private List<REQUESTING_PARTY> rEQUESTING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private RECEIVING_PARTY rECEIVING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private SUBMITTING_PARTY sUBMITTING_PARTYField;
[EditorBrowsable(EditorBrowsableState.Never)]
private REQUEST rEQUESTField;
[EditorBrowsable(EditorBrowsableState.Never)]
private string iDField;
public REQUEST_GROUP()
{
this.rEQUESTField = new REQUEST();
this.sUBMITTING_PARTYField = new SUBMITTING_PARTY();
this.rECEIVING_PARTYField = new RECEIVING_PARTY();
this.rEQUESTING_PARTYField = new List<REQUESTING_PARTY>();
this.IDField = "2.1";
}
}
Output from the Serialize with an encode of utf-8:
<?xml version="1.0" encoding="utf-8"?>
<REQUEST_GROUP xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="2.1" xmlns="Com.Foo.Request">
<RECEIVING_PARTY />
<SUBMITTING_PARTY />
<REQUEST LoginAccountIdentifier="xxx" LoginAccountPassword="yyy" _RecordIdentifier="" _JobIdentifier="">
<REQUESTDATA>
<PROPERTY_INFORMATION_REQUEST _SpecialInstructionsDescription="" _ActionType="Submit">
<_DATA_PRODUCT _ShortSubjectReport="Y" />
<_PROPERTY_CRITERIA _City="Sunshine City" _StreetAddress2="" _StreetAddress="123 Main Street" _State="CA" _PostalCode="12345">
<PARSED_STREET_ADDRESS />
</_PROPERTY_CRITERIA>
<_SEARCH_CRITERIA />
<_RESPONSE_CRITERIA />
</PROPERTY_INFORMATION_REQUEST>
</REQUESTDATA>
</REQUEST>
</REQUEST_GROUP>
EDIT
Question 1: How do I decorate the class in such a fashion, or manipulate the serializer to get rid of all the namespaces in the REQUEST_GROUP node during processing, NOT post-processing with xslt or regex.
Question 2: Bonus point if you could add the doc type too.
Thank you.
You can remove the namespaces like this:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
ns.Add(string.Empty, "Com.Foo.Request");
Serializer.Serialize(xmlWriter, this, ns);
As for adding the doctype, I know it's possible to make a custom XmlWriter and just override WriteStartDocument with a method that makes a call to WriteDocType, but I kind of hope someone else knows an easier way than that.
EDIT: Incidentally, I strongly recommend using using:
using(System.Xml.XmlWriter xmlWriter = XmlWriter.Create(etc.))
{
// use it here.
}
It automatically handles tidying up of the streams by calling the Dispose method when the block ends.
If you just want to remove the namespace aliases, then as already shown you can use XmlSerializerNamespaces to force XmlSerializer to use the namespace explicitly (i.e. xmlns="blah") on each element, rather than declaring an alias and using the alias instead.
However, regardless of what you do with the aliases, the fundamental name of that element is REQUEST_GROUP in the Com.Foo.Request namespace. You can't remove the namespace completely without that representing a breaking change to the underlying data - i.e. somebody somewhere is going to get an exception (due to getting data it didn't expect - specifically REQUEST_GROUP in the root namespace). In C# terms, it is the difference between System.String and My.Custom.String - sure, they are both called String, but that is just their local name.
If you want to remove all traces of the namespace, then a pragmatic option would be to edit away the Namespace=... entries from [XmlRoot(...)] and [XmlType(...)] (plus anywhere else that isn't shown in the example).
If the types are outside of your control, you can also do this at runtime using XmlAttributeOverrides - but a caveat: if you create an XmlSerializer using XmlAttributeOverrides you must cache and re-use it - otherwise your AppDomain will leak (it creates assemblies on the fly per serializer in this mode, and assemblies cannot be unloaded).
I am serializing an object in my ASP.net MVC program to an xml string like this;
StringWriter sw = new StringWriter();
XmlSerializer s = new XmlSerializer(typeof(mytype));
s.Serialize(sw, myData);
Now this give me this as the first 2 lines;
<?xml version="1.0" encoding="utf-16"?>
<GetCustomerName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
my question is,
How can I change the xmlns and the encoding type, when serializing?
Thanks
What I found that works was to add this line to my class,
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://myurl.com/api/v1.0", IsNullable = true)]
and add this to my code to add namespace when I call serialize
XmlSerializerNamespaces ns1 = new XmlSerializerNamespaces();
ns1.Add("", "http://myurl.com/api/v1.0");
xs.Serialize(xmlTextWriter, FormData, ns1);
as long as both namespaces match it works well.
The XmlSerializer type has a second parameter in its constructor which is the default xml namespace - the "xmlns:" namespace:
XmlSerializer s = new XmlSerializer(typeof(mytype), "http://yourdefault.com/");
To set the encoding, I'd suggest you use a XmlTextWriter instead of a straight StringWriter and create it something like this:
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
XmlTextWriter xtw = XmlWriter.Create(filename, settings);
s.Serialize(xtw, myData);
In the XmlWriterSettings, you can define a plethora of options - including the encoding.
Take a look at the attributes that control XML serialization in .NET.
Specifically, the XmlTypeAttribute may be useful for you. If you're looking to change the default namespace for you XML file, there is a second parameter to the XmlSerializer constructor where you can define that.