attribute not being serialized by XmlSerializer - c#

I'd like to serialize a class to XML, assigning an XML attribute to it. Snippet:
[XmlType(TypeName = "classmy")]
public class MyClass2 : List<object>
{
[XmlAttribute(AttributeName = "myattr")]
public string Name { get; set; }
}
public class MyConst
{
public MyConst()
{
MyClass2 myClass2 = new MyClass2 { 10, "abc" };
myClass2.Name = "nomm";
XmlSerializer serializer = new XmlSerializer(typeof(MyClass2));
serializer.Serialize(Console.Out, myClass2);
}
}
But the resulting XML looks like this
<?xml version="1.0" encoding="IBM437"?>
<classmy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<anyType xsi:type="xsd:int">10</anyType>
<anyType xsi:type="xsd:string">abc</anyType>
</classmy>
All well and good, with the only exception being that myClass2.Name is not serialized. I was expecting something in the line of
<classmy myattr="nomm" [...]>[...]</classmy>
... Why isn't that serialized, and how can it be?

dont derive List<T>, create class with member List
[XmlType(TypeName = "classmy")]
public class MyClass2
{
[XmlAttribute(AttributeName = "Items")]
List<object> Items { get; set; } //need to change type in `<>`
[XmlAttribute(AttributeName = "myattr")]
public string Name { get; set; }
}

Alternative solution: use an array instead of a list and XmlElement
[XmlType(TypeName = "classmy")]
public class MyClass2
{
[XmlElement(ElementName = "Items")]
public object[] Items { get; set; }
[XmlAttribute(AttributeName = "myattr")]
public string Name { get; set; }
}

XmlSerializer treats List<> in special way:
XmlSerializer can process classes that implement IEnumerable or ICollection differently if they meet certain requirements. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be consistent (polymorphic) with the type returned from the IEnumerator.Current property returned from the GetEnumerator method. A class that implements ICollection in addition to IEnumerable (such as CollectionBase) must have a public Item indexed property (an indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter passed to the Add method must be the same type as that returned from the Item property, or one of that type's bases. For classes implementing ICollection, values to be serialized will be retrieved from the indexed Item property rather than by calling GetEnumerator. Also note that public fields and properties will not be serialized, with the exception of public fields that return another collection class (one that implements ICollection). MSDN - scroll to XML Serialization Considerations
That why it serialized Your class as a list of objects only, without Your property. The best solution is to include List as class public property and mark it as XmlElement.

Related

Json.net `JsonConstructor` constructor parameter names

When using a specific .ctor via JsonConstructor for deserializing IList<ISomeInterface> properties, the parameter names must match the original Json names and the JsonProperty mapping on those properties are not used.
Example:
SpokenLanguages parameter is always null since it does not match spoken_languages, but there is a JsonProperty mapping it:
public partial class AClass : ISomeBase
{
public AClass() { }
[JsonConstructor]
public AClass(IList<SysType> SysTypes, IList<ProductionCountry> production_countries, IList<SpokenLanguage> SpokenLanguages)
{
this.Genres = SysTypes?.ToList<IGenre>();
this.ProductionCountries = production_countries?.ToList<IProductionCountry>();
this.SpokenLanguages = SpokenLanguages?.ToList<ISpokenLanguage>();
}
public int Id { get; set; }
public IList<IGenre> Genres { get; set; }
[JsonProperty("production_countries")]
public IList<IProductionCountry> ProductionCountries { get; set; }
[JsonProperty("spoken_languages")]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
}
Is this just a "limitation" of how Json.Net calls the constructor or is there something I am missing.
FYI: I am code generating all this via Rosyln and am not looking at generating a JsonConverter for each type for this...
When Json.NET invokes a parameterized constructor, it matches JSON properties to constructor arguments by name, using an ordinal case-ignoring match. However, for JSON properties that also correspond to type members, which name does it use - the member name, or the override type member name specified by JsonPropertyAttribute.PropertyName?
It appears you are hoping it matches on both, since your argument naming conventions are inconsistent:
The constructor argument production_countries matches the overridden property name:
[JsonProperty("production_countries")]
public IList<IProductionCountry> ProductionCountries { get; set; }
The constructor argument IList<SpokenLanguage> SpokenLanguages matches the reflected name rather than the overridden property name:
[JsonProperty("spoken_languages")]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
IList<SysType> SysTypes matches neither (is this a typo in the question?)
However, what matters is the property name in the JSON file itself and the constructor argument name as shown in JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(). A simplified version of the algorithm is as follows:
The property name is read from the JSON file.
A closest match constructor argument is found (if any).
A closest match member name is found (if any).
If the JSON property matched a constructor argument, deserialize to that type and pass into the constructor,
But if not, deserialize to the appropriate member type and set the member value after construction.
(The implementation becomes complex when a JSON property matches both and developers expect that, for instance, [JsonProperty(Required = Required.Always)] added to the member should be respected when set in the constructor.)
Thus the constructor argument production_countries will match a value named "production_countries" in the JSON, while the constructor argument SpokenLanguages will not match a JSON value named "spoken_languages".
So, how to deserialize your type successfully? Firstly, you could mark the constructor parameters with [JsonProperty(overrideName)] to override the constructor name used during deserialization:
public partial class AClass : ISomeBase
{
public AClass() { }
[JsonConstructor]
public AClass([JsonProperty("Genres")] IList<SysType> SysTypes, IList<ProductionCountry> production_countries, [JsonProperty("spoken_languages")] IList<SpokenLanguage> SpokenLanguages)
{
this.Genres = SysTypes == null ? null : SysTypes.Cast<IGenre>().ToList();
this.ProductionCountries = production_countries == null ? null : production_countries.Cast<IProductionCountry>().ToList();
this.SpokenLanguages = SpokenLanguages == null ? null : SpokenLanguages.Cast<ISpokenLanguage>().ToList();
}
Secondly, since you seem to be using the constructor to deserialize items in collections containing interfaces as concrete objects, you could consider using a single generic converter based on CustomCreationConverter as an ItemConverter:
public partial class AClass : ISomeBase
{
public AClass() { }
public int Id { get; set; }
[JsonProperty(ItemConverterType = typeof(CustomCreationConverter<IGenre, SysType>))]
public IList<IGenre> Genres { get; set; }
[JsonProperty("production_countries", ItemConverterType = typeof(CustomCreationConverter<IProductionCountry, ProductionCountry>))]
public IList<IProductionCountry> ProductionCountries { get; set; }
[JsonProperty("spoken_languages", ItemConverterType = typeof(CustomCreationConverter<ISpokenLanguage, SpokenLanguage>))]
public IList<ISpokenLanguage> SpokenLanguages { get; set; }
}
public class CustomCreationConverter<T, TSerialized> : CustomCreationConverter<T> where TSerialized : T, new()
{
public override T Create(Type objectType)
{
return new TSerialized();
}
}
Example fiddle showing both options.

How to solve "Both use the XML type name X, use XML attributes to specify a unique XML name and/or namespace for the type"?

I have the following enum definitions...
namespace ItemTable
{
public enum DisplayMode
{
Tiles,
Default
}
}
namespace EffectiveItemPermissionTable
{
public enum DisplayMode
{
Tree,
FullPaths
}
}
...and then i have the following classes...
public class Table<TDisplayMode>
where TDisplayMode: struct
{
// public
public TDisplayMode DisplayMode
{
get { return mDisplayMode; }
set { mDisplayMode = value; }
}
// private
private TDisplayMode mDisplayMode;
}
public class ItemTable : Table<ItemTable.DisplayMode>
{}
public class EffectiveItemPermissionTable : Table<EffectiveItemPermissionTable.DisplayMode>
{}
public class UISettings
{
public UISettings()
{
ItemTable = new ItemTable();
EffectiveItemPermissionTable = new EffectiveItemPermissionTable();
}
public ItemTable ItemTable { get; set; }
public EffectiveItemPermissionTable EffectiveItemPermissionTable { get; set; }
}
...and when i try to serialize an instance of UISettings with...
System.Xml.Serialization.XmlSerializer lSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(UISettings));
...i get the following error:
Types 'UISettings.Table`1[EffectiveItemPermissionTable.DisplayMode]' and
'UISettings.Table`1[ItemTable.DisplayMode]' both use the XML type name,
'TableOfDisplayMode', from namespace ''.
Use XML attributes to specify a unique XML name and/or namespace for the type.
I have tried to use XmlType attribubtes and all sorts of solutions posted on the web but nothing works. The XML type name is always TableOfDisplayMode as mentioned in the error.
The only solution right now is to rename one of the enums, e.g. to DisplayMode_ but I find that rather ugly.
You need to provide the Namespace by using the XmlElement attribute on the properties of your UISettings class:
public class UISettings
{
public UISettings()
{
ItemTable = new ItemTable();
EffectiveItemPermissionTable = new EffectiveItemPermissionTable();
}
[XmlElement(Namespace = "Item")]
public ItemTable ItemTable { get; set; }
[XmlElement(Namespace = "Permissions")]
public EffectiveItemPermissionTable EffectiveItemPermissionTable { get; set; }
}
When applied here this will be your serialized output:
<?xml version="1.0" encoding="utf-16"?>
<UISettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ItemTable xmlns="Item">
<DisplayMode>Tiles</DisplayMode>
</ItemTable>
<EffectiveItemPermissionTable xmlns="Permissions">
<DisplayMode>FullPaths</DisplayMode>
</EffectiveItemPermissionTable>
</UISettings>
Alternatively, and maybe cleaner, you can provide the Namespace on the types:
[XmlType(Namespace="Item")]
public class ItemTable : Table<ItemTableNS.DisplayMode>
{ }
[XmlType(Namespace = "Permission")]
public class EffectiveItemPermissionTable : Table<EffectiveItemPermissionTableNS.DisplayMode>
{ }
This will serialize as:
<?xml version="1.0" encoding="utf-16"?>
<UISettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ItemTable>
<DisplayMode xmlns="Item">Tiles</DisplayMode>
</ItemTable>
<EffectiveItemPermissionTable>
<DisplayMode xmlns="Permission">FullPaths</DisplayMode>
</EffectiveItemPermissionTable>
</UISettings>
I realize this answer probably comes way too late for the OP, but there is a way to do this without using namespaces, so I'm going to leave an answer here in case somebody comes along after me and needs the solution.
The problem is caused by the fact that the way the XmlSerializer names a type X<Y> is by giving it the name XOfY. Thus, when you have two types that both derive from Table<TDisplayMode>, you get that error, since they'll both be known internally as TableOfDisplayMode, despite actually using different enums.
This is because ItemTable and EffectiveItemPermissionTableare actually not inheriting from the same type! One inherits from Table<ItemTable.DisplayMode> and the other from Table<EffectiveItemPermissionTable.DisplayMode>. Not that this is limited to inheritance; you'd face the same problem if you tried to use them directly in the same XML object graph also.
Now, for the non-generic counterpart to this problem, you'd just smack an [XmlType] on the two types, and call it a day. But you can't do that here. While Table<ItemTable.DisplayMode> and Table<EffectiveItemPermissionTable.DisplayMode> are different types, they share the same class definition, so by trying to use [XmlType], you're giving them a different name, but still the same name.
So what can you do? XmlAttributeOverrides to the rescue! It lets you override the names the XmlSerializer gives to closed generic types, meaning that you can finally give a different name to Table<ItemTable.DisplayMode> and Table<EffectiveItemPermissionTable.DisplayMode>:
var xmlOverrides = new XmlAttributeOverrides();
var xmlAttribs = new XmlAttributes();
xmlAttribs.XmlType = new XmlTypeAttribute("TableOfItemTableDisplayMode");
xmlOverrides.Add(typeof(Table<ItemTable.DisplayMode>), xmlAttribs);
xmlAttribs = new XmlAttributes();
xmlAttribs.XmlType = new XmlTypeAttribute("TableOfEffectiveItemPermissionTableDisplayMode");
xmlOverrides.Add(typeof(Table<EffectiveItemPermissionTable.DisplayMode>), xmlAttribs);
System.Xml.Serialization.XmlSerializer lSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(UISettings), xmlOverrides);
And voilĂ ! Assuming now that you've also put [XmlType] with different names for your DisplayMode enums, so that their names don't conflict either, you've got yourself a working setup!

Deserialize objects with RestSharp where the class is in a type attribute

I'm trying to consume data from a rest api.
I need to deserialize objects from a rest API using RestSharp.
The all objects in lists are an "object" element where the object's class is the value of a "type" attribute.
Here is an example of what I mean:
<list>
<object type="cat">
<id>107</id>
<name>Garry</name>
</object>
<object type="dog">
<id>83</id>
<name>Fluffy</name>
</object>
</list>
And partially implemented classes for the example:
public class Animal
{
public long Id { get; set; }
public string Name { get; set; }
}
[DeserializeAs(Name = "cat")]
public class Cat : Animal
{
}
[DeserializeAs(Name = "dog")]
public class Dog : Animal
{
}
It seems like the wrong way to go, but tried defining all of my classes using the attribute:
[DeserializeAs(Name = "object")]
This allows them to deserialize properly, as long as I know what type of object to expect in the list, and obviously the list only contains one type of object.
The problem comes in if I get a list containing different types of objects.
Is there a way to handle this well using the standard deserializer?
If not, I am open to ways to handle this effectively with a large number of different object types.
I looked at the standard deserializer. I am pretty sure it was not designed to handle this. I ended up making my own fork.
I added another attribute for describing the object in lists:
[DeserializeElementAs(Name = "", Attribute = "", Value = "")]
The attribute name might not be the best, but I was not sure what to call it.
So the classes for the xml from the question would be defined as:
public class Animal
{
public long Id { get; set; }
public string Name { get; set; }
}
[DeserializeElementAs(Name = "object", Attribute = "type", Value = "cat")]
[DeserializeAs(Name = "cat")]
public class Cat : Animal
{
}
[DeserializeElementAs(Name = "object", Attribute = "type", Value = "dog")]
[DeserializeAs(Name = "dog")]
public class Dog : Animal
{
}
I posted the code of fork to github if anyone else needs it.
https://github.com/comless/RestSharp/commit/1fea0fd97cadc7035dbea99c17b7423ca14b5267

How to Deserialize XML elements to generic list with unknown element names

I am retrieving and successfully deserializing an xml string from a database table column for the known element names (these do not change) but there are also some nested XML Elements called "Other Attributes" which are not always going to be known. I'm having some trouble deserialising these unknown element names to a generic list so I can display them in html once deserialised.
The XML is as follows:
<Detail>
<DetailAttributes>
<Name>Name_123</Name>
<Type>Type_123</Type>
</DetailAttributes>
<OtherAttributes>
<SummaryKey AttributeName="SummaryKey">SummaryKey_123</SummaryKey>
<Account AttributeName="Account">Account_123</Account>
</OtherAttributes>
</Detail>
I have no problem deserialising the 'Name' and 'Type' elements and I can deserialise the 'SummaryKey' and 'Account' elements but only if I explicitly specify their element names - which is not the desired approach because the 'OtherAttributes' are subject to change.
My classes are as follows:
[XmlRoot("Detail")]
public class objectDetailsList
{
[XmlElement("DetailAttributes"), Type = typeof(DetailAttribute))]
public DetailAttribute[] detailAttributes { get; set; }
[XmlElement("OtherAttributes")]
public List<OtherAttribute> otherAttributes { get; set; }
public objectDetailsList()
{
}
}
[Serializable]
public class Detail Attribute
{
[XmlElement("Type")]
public string Type { get;set; }
[XmlElement("Name")]
public string Name { get;set; }
public DetailAttribute()
{
}
}
[Serializable]
public class OtherAttribute
{
//The following will deserialise ok
//[XmlElement("SummaryKey")]
//public string sumKey { get; set; }
//[XmlElement("Account")]
//public string acc { get; set; }
//What I want to do below is create a list of all 'other attributes' without known names
[XmlArray("OtherAttributes")]
public List<Element> element { get; set; }
}
[XmlRoot("OtherAttributes")]
public class Element
{
[XmlAttribute("AttributeName")]
public string aName { get; set; }
[XmlText]
public string aValue { get; set; }
}
When I try to retrieve the deserialised list of OtherAttribute elements the count is zero so it's not able to access the elements nested within "Other Attributes".
Can anybody help me with this please?
With concrete classes and dynamic data like this, you won't be able to lean on the standard XmlSerializer to serialize / deserialize for you - as it reflects on your classes, and the properties you want to populate simply don't exist. You could provide a class with all possible properties if your set of 'OtherAttributes' is known and finite, and not subject to future change, but that would give you an ugly bloated class (and I think you've already decided this is not the solution).
Practical options therefore:
Do it manually. Use the XmlDocument class, load your data with .Load(), and iterate the nodes using .SelectNodes() using an XPath query (something like "/Detail/OtherAttributes/*"). You will have to write the lot yourself, but this gives you complete control over the serialization / deserialization. You won't have to cover your code in (arguably superfluous!) attributes either.
Use Json.NET (http://james.newtonking.com/json), it allows for far greater control over serialization and deserialization. It's fast, has good docs and is overall pretty nifty really.

Serialize subclass with Json.NET

I am trying to use Json.NET to serialize a subclass. The resulting json contains the serialized properties for the superclass but not the properties on the subclass object.
This seems to be related to an issue I found here on SO. But having to write a JsonConverter seems like overkill.
Sample subclass:
public class MySubclass : List<string>
{
public string Name { get; set; }
}
Sample of the serialization:
MySubclass myType = new MySubclass() { Name = "Awesome Subclass" };
myType.Add("I am an item in the list");
string json = JsonConvert.SerializeObject(myType, Newtonsoft.Json.Formatting.Indented);
Resulting json:
[
"I am an item in the list"
]
I expected to result to be more like this:
{
"Name": "Awesome Subclass",
"Items": [
"I am an item in the list"
]
}
Perhaps I am just not using the right configuration when serializing. Anyone have any suggestions?
According the documentation:
.NET lists (types that inherit from IEnumerable) and .NET arrays are
converted to JSON arrays. Because JSON arrays only support a range of
values and not properties, any additional properties and fields
declared on .NET collections are not serialized.
So, don't subclass List<T>, just add a second property.
public class MyClass
{
public List<string> Items { get; set; }
public string Name { get; set; }
public MyClass() { Items = new List<string>(); }
}
Here are my thoughts on this. I would think that your expected results would be more consistent with a class like this:
public class MyClass
{
public string Name { get; set; }
public List<string> Items { get; set; }
}
I would not expect to see an Items element in the serialized result for MySubclass : List<string> since there is no Items property on List nor on MySubclass.
Since your class MySubclass is actually a list of strings, I would guess (and I am just guessing here) that the SerializeObject method is merely iterating through your object and serializing a list of strings.

Categories