Summary
When using the XmlSerializer
class, serializing a List<T>
(where T can be serialized with XmlSerializer
without problems) using XmlAttributeOverrides
such as this:
using xmls = System.Xml.Serialization;
...
xmls.XmlAttributeOverrides attributeOverrides = new xmls.XmlAttributeOverrides();
attributeOverrides.Add(typeof(T), new xmls.XmlAttributes()
{
XmlRoot = new xmls.XmlRootAttribute("foo")
});
attributeOverrides.Add(typeof(List<T>), new xmls.XmlAttributes()
{
XmlArray = new xmls.XmlArrayAttribute("foobar"),
XmlArrayItems = { new xmls.XmlArrayItemAttribute("foo") },
});
will throw the following InvalidOperationExcpetion at the inner-most exception:
System.InvalidOperationException: XmlRoot and XmlType attributes may not be specified for the type System.Collections.Generic.List`1[[T, programname, Version=versionnumber, Culture=neutral, PublicKeyToken=null]].
What I expect from the serializer
<texparams>
<texparam pname="TextureMinFilter" value="9729"/>
<texparam pname="TextureMagFilter" value="9729"/>
</texparams>
What I can sucessfully get at the moment
<ArrayOfTextureParameter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TextureParameter pname="TextureMinFilter" value="9729" />
<TextureParameter pname="TextureMagFilter" value="9728" />
</ArrayOfTextureParameter>
Background Info
I have been messing around with XML Serialization lately but have come across a problem. I am trying to serialize and deserialize some classes that wrap OpenGL textures.
In one of my classes (which I appropriately called BitmapTexture2d
) I have a Bitmap
field, which I wish to store in a Base64 element like so:
<bitmap64>
*base64goeshere*
</bitmap64>
Since I want to keep my code as neat as possible I decided to use the IXmlSerializable
interface instead of creating a property which can convert a string
and a Bitmap
back and forth.
Later on in the process I decided to use the XmlSerializer
class to generate the XML for a single field defined in Texture2d
(which BitmapTexture2d
is derived from) called Parameters
(which is a List<TextureParameter>
and TextureParameter
is serializable by the XmlSerialization
class). However this is how the serializer defaulted to serializing the List<TextureParameter>
:
<ArrayOfTextureParameter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TextureParameter pname="TextureMinFilter" value="9729" />
<TextureParameter pname="TextureMagFilter" value="9728" />
</ArrayOfTextureParameter>
After seeing this, I decided to try and change the names of the nodes. After some research (where I landed at stackoverflow multiple times) I discovered the XmlAttributeOverrides
class which can be passed to the constructor of XmlSerializer
to add/override node names etc.
After writing out my code, the constructor for the sub-serializer started throwing an exception as described above. I have tried using an array which threw the same exception. I later though to serialize each element in the list one by one, but came to the conclusion that it was harder than I thought to achieve it that way. I posted this question somewhere else with no answer. And here I am...
Your problem is that you are trying to use overrides to attach [XmlArray]
and [XmlArrayItem]
to the type List<T>
. However, as shown in the docs , [XmlArray]
cannot be used in this manner:
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false)] public class XmlArrayAttribute : Attribute
Notice there is no AttributeTargets.Class
included? That means that [XmlArray]
cannot be applied directly to a type and so attempting to do so via XML overrides rightly throws an exception.
But as to why that exception message states,
System.InvalidOperationException: XmlRoot and XmlType attributes may not be specified for the type System.Collections.Generic.List`1...
Er, well, that message is simply wrong. It would appear to be a minor bug in XmlSerializer
. You could even report it to Microsoft if you want.
What you need to do instead is:
Override the [XmlRoot]
attribute of List<T>
to specify the desired name, in this case "texparams", AND
Override the [XmlType]
attribute of T
and set XmlTypeAttribute.TypeName
to be the desired collection element name. In the absence of an [XmlArrayItem(name)]
override this is what controls the element names of collections whose items are of type T
.
Thus your code should look like:
static XmlSerializer MakeListSerializer<T>(string rootName, string elementName)
{
xmls.XmlAttributeOverrides attributeOverrides = new xmls.XmlAttributeOverrides();
attributeOverrides.Add(typeof(List<T>), new xmls.XmlAttributes()
{
XmlRoot = new xmls.XmlRootAttribute(rootName),
});
attributeOverrides.Add(typeof(T), new xmls.XmlAttributes()
{
XmlType = new xmls.XmlTypeAttribute(elementName),
});
return new XmlSerializer(typeof(List<T>), attributeOverrides);
}
Sample fiddle .
Note that when constructing an XmlSerializer
using XmlAttributeOverrides
you must cache the serializer for later reuse to avoid a severe memory leak, for reasons explained here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.