DataContractJsonSerializer and List<Interface> - c#

In my code I have to serialize List<IModel> where IModel is the interface for concrete class Model
Here is some pseudo code for them:
public interface IModel
{
string Codice { get; set; }
int Position { get; }
}
[DataContract]
public class Model : IModel
{
public Model(string Codice, int position)
{
this.Codice = Codice;
this.position = position;
}
[DataMember(Name = "codice")]
public string Codice { get; set; }
[DataMember(Name = "position")]
int position;
public int Position { get { return position; } }
}
After reading this post I wrote:
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(List<IModel>), new[] { typeof(Model) });
using (FileStream writer = File.Create(#"c:\temp\modello.json"))
{
jsonSerializer.WriteObject(writer, myList);
}
It works but it is ugly and the output contains a field for the type of the element, "__type":"Model:#SomeProjectName".
Is there another way to easily serialize a list when it is declared as List<InterfaceName> while it contains elements of the only concrete class that implement that interface ? I tried with some casts but I got compilation error or runtime exceptions.
I would like to specify that in my previous implementation I copied all items from List<IModel> into List<Model> which was the type known to DataContractJsonSerializer. In fact I'm sure that any IModel is a Model.

Why do you need an interface for any such implementation?
I would suggest you to generate C# classes for your JSON through http://json2csharp.com/. Once done , paste those classes and find the <RootObject> class and then serialize it using the code you mentioned in your question

Here is a solution with AutoMapper. I've changed your implemented class by adding a private setter to Position. I hope this is OK for your.
List<IModel> sourceList = new List<IModel> {new Model("A", 1), new Model("B", 2)};
AutoMapper.Mapper.Initialize(a => a.CreateMap<IModel, Model>());
List<Model> targetList = AutoMapper.Mapper.Map<List<IModel>, List<Model>>(sourceList);
AutoMapper.Mapper.Initialize(a =>
{
a.CreateMap<Model, Model>();
a.CreateMap<Model, IModel>().ConstructUsing(Mapper.Map<Model, Model>);
});
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(List<Model>), new[] { typeof(Model) });
using (FileStream writer = File.Create(#"c:\temp\modello.json"))
{
jsonSerializer.WriteObject(writer, targetList);
}
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(List<Model>));
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText(#"c:\temp\modello.json")));
List<Model> targetListFromFile = (List<Model>)js.ReadObject(ms);
List<IModel> sourceListFromFile = AutoMapper.Mapper.Map<List<Model>, List<IModel>>(targetListFromFile);
Interface:
public interface IModel
{
string Codice { get; set; }
int Position { get; }
}
Class:
[DataContract]
public class Model : IModel
{
public Model(string Codice, int position)
{
this.Codice = Codice;
Position = position;
}
[DataMember(Name = "codice")]
public string Codice { get; set; }
[DataMember(Name = "position")]
public int Position { get; private set; }
}
The file looks like this:
[{
"codice": "A",
"position": 1
},
{
"codice": "B",
"position": 2
}]

Related

How do I get DataContractJsonSerializer to use concrete type in type hint when serializing generic class from interface

I have a set of classes as follows: a Command, which Executes and stores a Result;
a Response, which is created as in order to return the Result in a serialized form (plus extra metadata which I've left out). The Response.Result must be of type object, as it is used for a bunch of different commands, each of which can have a Result of any type at all.
The Command is generic, and I'd like it to accept an interface rather than concrete type, but when I do, the serialized response contains the following type hint: "__type":"ResultOfanyType:#serialization"
rather than the following, which is generated when the command accepts a concrete type:
"__type":"ResultOfMyObjectDhOQ6IBI:#serialization"
I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context? Notice that when the Type is a direct property of the serialized Command, then the concrete type is contained in the type hint
I've tried changing the the Result's Response property typed to Result, but that has no effect.
Here is the code. Simply uncomment/comment the lines in Main where the command is created and known types listed for the alternative version.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
namespace serialization
{
class Program
{
static void Main(string[] args)
{
Response response = new Response();
response.ResponseStatus = "ok";
ConcreteCommand command = new ConcreteCommand(); //switch with line below to test inteface
//InterfaceCommand command = new InterfaceCommand();
command.Execute();
response.Results = command.Results;
List<Type> knownTypes = new List<Type>
{
typeof(Result<MyObject>), //switch with Interface lines below to test inteface
typeof(MyObject)
//typeof(Result<IMyObject>),
//typeof(IMyObject)
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
Stream stream = new MemoryStream();
serializer.WriteObject(stream, response);
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
string output = reader.ReadToEnd();
Console.WriteLine(output);
}
}
public interface IMyObject
{
string name { get; set; }
}
[DataContract]
[KnownType(typeof(MyObject))]
public class MyObject : IMyObject
{
[DataMember]
public string name { get; set; }
}
[DataContract]
public class Result<T>
{
[DataMember]
public string Status { get; set; }
[DataMember]
public T Item { get; set; }
}
public abstract class BaseCommand<T>
{
protected Result<T> results = new Result<T>();
protected T resultObject;
public object Results
{
get { return this.results; }
}
public T ResultObject
{
get { return this.resultObject; }
}
public abstract void Execute();
}
public class InterfaceCommand : BaseCommand<IMyObject>
{
public override void Execute()
{
IMyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results= result;
this.resultObject = myobject;
}
}
public class ConcreteCommand : BaseCommand<MyObject>
{
public override void Execute()
{
MyObject myobject = new MyObject();
myobject.name = "my object";
Result<MyObject> result = new Result<MyObject>();
result.Item = myobject;
result.Status = "ok";
this.results = result;
this.resultObject = myobject;
}
}
[DataContract]
public class Response
{
[DataMember]
public string ResponseStatus { get; set; }
[DataMember]
public object Results { get; set; }
}
}
Let's start with this question and that might explain everything.
I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context?
An interface is basically just a contract for what a class implementing it should contain and multiple classes could implement its members. For example.
public interface IPerson
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
public class Person : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Contact : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string PhoneNumber { get; set; }
}
So when you call an IPerson what are you expecting back? A Person or a Contact? Each has an id and the basic components of a name, but each also has unique properties that IPerson doesn't even know exist. This is why when you try to get an interface to resolve to a concrete class, you're not going to get anywhere without some sort of factory class to figure out what you want. So in this case, if I wanted to resolve an IPerson, I'd add the following line of code...
var objectType = iPersonObject.GetType();
In your case, you'd want to try calling GetType() on result.Item. This tells .NET to look at the actual type of the object implementing the interface and return it.
How about this...
class Program
{
static void Main(string[] args)
{
Response response = new Response();
response.ResponseStatus = "ok";
//ConcreteCommand command = new ConcreteCommand(); //switch with line below to test inteface
InterfaceCommand command = new InterfaceCommand();
command.Execute();
response.Results = command.Results;
List<Type> knownTypes = new List<Type>
{
typeof(MyObject),
typeof(Result<MyObject>) //switch with line below to test inteface
//typeof(Result<IMyObject>)
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
Stream stream = new MemoryStream();
serializer.WriteObject(stream, response);
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
string output = reader.ReadToEnd();
Console.WriteLine(output);
}
}
public interface IMyObject
{
string name { get; set; }
}
[DataContract]
public class MyObject : IMyObject
{
[DataMember]
public string name { get; set; }
}
[DataContract]
public class Result<T>
{
[DataMember]
public string Status { get; set; }
[DataMember]
public T Item { get; set; }
}
public abstract class BaseCommand
{
protected Result<IMyObject> results = new Result<IMyObject>();
public Result<IMyObject> Results
{
get { return this.results; }
}
public abstract void Execute();
}
public class InterfaceCommand : BaseCommand
{
public override void Execute()
{
IMyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results= result;
}
}
public class ConcreteCommand : BaseCommand
{
public override void Execute()
{
MyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results = result;
}
}
[DataContract]
public class Response
{
[DataMember]
public string ResponseStatus { get; set; }
[DataMember]
public Result<IMyObject> Results { get; set; }
}
Outputs...
{"__type":"Response:#ConsoleApplication2","ResponseStatus":"ok","Results":{"__ty
pe":"ResultOfanyType:#ConsoleApplication2","Item":{"__type":"MyObject:#ConsoleAp
plication2","name":"my object"},"Status":"ok"}}
If you're trying to make some sort of generic contract, you're going to have to have some sort of common base class/interface. It won't work with object but you can go ala COM and make your own IUnknown interface from which to create as many subclasses as you like, as long as they are included within your known types.

C# XML Deserialization of array

I have a problem about deserialize an array. Becouse array elements can be of various types. You can see the example:
<?xml version="1.0" encoding="UTF-8"?><export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://zakupki.gov.ru/oos/export/1" xmlns:oos="http://zakupki.gov.ru/oos/types/1">
<notificationZK>
... item 1 data
</notificationZK>
<notificationZK>
... item 2 data
</notificationZK>
<notificationFF>
... item 3 data
</notificationFF>
</export>
All elements extends notificationType
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationSZType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationPOType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationZKType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationEFType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationOKType))]
public partial class notificationType
{
...
So the question is how can I get the collection of notificationType elements from my XML file? I think I cant do something like
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlArray("")] // ???? what I need write here?
[XmlArrayItem("", typeof(notificationType))] // ??? and here?
public notificationType[] notification { get; set; }
}
Regards!
ADDED-------------
So. I make this:
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
class Program
{
static void Main(string[] args)
{
NotificationCollection collection = null;
string path = #"E:\notification.xml";
XmlSerializer serializer = new XmlSerializer(typeof(notificationType));
StreamReader reader = new StreamReader(path);
collection = (NotificationCollection) serializer.Deserialize(reader);
reader.Close();
}
}
but have System.InvalidOperationException was unhandled while serializer.Deserialize(reader);
Message=<export xmlns='http://zakupki.gov.ru/oos/export/1'> not expected.
What im doing wrong?
How about moving the type declarations into the collection?
[XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationZK", typeof(NotificationTypeZK))]
[XmlElement("notificationFF", typeof(NotificationTypeFF))]
public List<NotificationType> Notifications { get; set; }
}
public class NotificationType
{
}
public class NotificationTypeZK : NotificationType { }
public class NotificationTypeFF : NotificationType { }
static void Main(string[] args)
{
var data = #"<export><notificationZK /><notificationZK /><notificationFF /></export>";
var serializer = new XmlSerializer(typeof(NotificationCollection));
using (var reader = new StringReader(data))
{
var notifications = serializer.Deserialize(reader);
}
}
This should do the job
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
This question is interesting for me as well. I wrote the simplified app to achieve what you ask for:
[Serializable]
[XmlInclude(typeof(ItemA))]
[XmlInclude(typeof(ItemB))]
public class BaseItem
{
public bool Value { get; set; }
}
[Serializable]
public class ItemA : BaseItem
{
public string Text { get; set; }
}
[Serializable]
public class ItemB : BaseItem
{
public int Number { get; set; }
}
[Serializable]
public class ItemsArray
{
public BaseItem[] Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var array = new ItemsArray
{
Items = new BaseItem[]
{
new ItemA { Value = true, Text = "Test" },
new ItemB { Value = false, Number = 7 }
}
};
ItemsArray output;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(ItemsArray));
serializer.Serialize(stream, array);
stream.Position = 0;
output = (ItemsArray)serializer.Deserialize(stream);
}
}
}
After deserialization we get exactly what we serialized. The XML inside stream looks like:
<?xml version="1.0"?>
<ItemsArray xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem xsi:type="ItemA">
<Value>true</Value>
<Text>Test</Text>
</BaseItem>
<BaseItem xsi:type="ItemB">
<Value>false</Value>
<Number>7</Number>
</BaseItem>
</Items>
</ItemsArray>
As was mentioned in other answer, you can't use different tags inside XML array. However, it's still possible to store different types. Serializer does this by using xsi:type attribute.
In order to solve your problem you probably need to use a bit another scheme of XML.

Create JSON with C# Object

I am trying to create the following JSON data:
{
'chart.labels': ['Bob','Lucy','Gary','Hoolio'],
'chart.tooltips': ['Bob did well',
'Lucy had her best result',
'Gary - not so good',
'Hoolio had a good start'
]
}
I am using C# and trying to create an object in order to do this.....something like:
public class chart{
public string[] chart.labels {get;set;}
public string[] chart.tooltips {get;set;}
}
but obviously I cannot have properties containing spaces.
How would I go about doing this ?
UPDATE:
Using JamieC's answer the following works perfecly
public virtual ActionResult CompanyStatus()
{
var labelList = new List<string>() { "Bob", "Lucy", "Gary", "Hoolio" };
var tooltipsList = new List<string>() { "Bob did well", "Lucy had her best result", "Gary - not so good", "Hoolio had a good start" };
var cData = new chartData()
{
Labels = labelList.ToArray(),
Tooltips = tooltipsList.ToArray()
};
var serializer = new DataContractJsonSerializer(cData.GetType());
String output;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, cData);
output = Encoding.Default.GetString(ms.ToArray());
}
return this.Content(output);
}
[DataContract]
public class chartData
{
[DataMember(Name = "chart.labels")]
public string[] Labels { get; set; }
[DataMember(Name = "chart.tooltips")]
public string[] Tooltips { get; set; }
}
}
Which produces:
{"chart.labels":["Bob","Lucy","Gary","Hoolio"],"chart.tooltips":["Bob did well","Lucy had her best result","Gary - not so good","Hoolio had a good start"]}
The usual way to do this is to use a DataContractJsonSerializer to turn your object into Json, and use DataMember attributes to annotate what names to use for properties:
[DataContract]
public class ChartModel{
[DataMember(Name = "chart.labels")]
public string[] Labels {get;set;}
[DataMember(Name = "chart.tooltips")]
public string[] Tooltips {get;set;}
}
I personally use my own ActionResult to wrap up the serialization in MVC:
public class JsonDataContractResult : ActionResult
{
public JsonDataContractResult(Object data)
{
this.Data = data;
}
protected JsonDataContractResult()
{
}
public Object Data { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
Guard.ArgumentNotNull(context, "context");
var serializer = new DataContractJsonSerializer(this.Data.GetType());
String output;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, this.Data);
output = Encoding.Default.GetString(ms.ToArray());
}
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.Write(output);
}
}
And return that from a helper method in a base controller:
public abstract class MyBaseController: Controller
{
protected JsonDataContractResult JsonContract(Object data)
{
return new JsonDataContractResult(data);
}
}
Then my controller becomes really simple:
public class SomeController: MyBaseController
{
public ActionResult SomeAction()
{
var model = new ChartModel()
{
Labels = ...,
Tooltips = ...
};
return JsonContract(model);
}
}
You can use JSON.NET library, you can download it from here
It has this feature:
Attribute property name customization
This question will help you:
Json.Net: JsonSerializer-Attribute for custom naming
And you can use DataContractJsonSerializer it provides this feature, but JavaScriptSerializer is not.
For MVC project Newtonsoft.Json library is available.(You have to manually include this for other projects)
So give JsonProperty in the model, like this...
public class ChartModel{
[JsonProperty("chart.labels")]
public string[] Labels {get;set;}
[JsonProperty("chart.tooltips")]
public string[] Tooltips {get;set;}
}
and use Newtonsoft.Json.JsonConvert.SerializeObject(object); or Json.Encode(object) to convert to JSON.

Deserialize part of a binary file

Is it possible to deserialize part of a binary file?
Basically I have an object similar to below, which I serialize into a binary file.
public class MyObject
{
public string Name { get; set; }
public int Value { get; set; }
public IList<MyOtherObject> { get; set; } // lots of data in here (order of kB-MB)
}
What I would like is to be able to deserialize only Name and Value by way of populating a ListView for file selection purposes and then deserialize the rest of the file when needed (i.e. the user chooses that file from the ListView).
As always, any help greatly appreciated and if any 3rd party libraries are suggested they would need to be able to be used freely in a commercial environment.
protobuf-net can do that, because it is not tied to the specific type; for example:
using ProtoBuf;
using System.Collections.Generic;
using System.IO;
[ProtoContract]
public class MyOtherObject { }
[ProtoContract]
public class MyObject
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Value { get; set; }
[ProtoMember(3)]
public IList<MyOtherObject> Items { get; set; }
}
[ProtoContract]
public class MyObjectLite
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Value { get; set; }
}
static class Program
{
static void Main()
{
var obj = new MyObject
{
Name = "abc",
Value = 123,
Items = new List<MyOtherObject>
{
new MyOtherObject(),
new MyOtherObject(),
new MyOtherObject(),
new MyOtherObject(),
}
};
using (var file = File.Create("foo.bin"))
{
Serializer.Serialize(file, obj);
}
MyObjectLite lite;
using (var file = File.OpenRead("foo.bin"))
{
lite= Serializer.Deserialize<MyObjectLite>(file);
}
}
}
But if you don't want two different types, and/or you don't want to have to add attributes - that can be done too:
using ProtoBuf.Meta;
using System.Collections.Generic;
using System.IO;
public class MyOtherObject { }
public class MyObject
{
public string Name { get; set; }
public int Value { get; set; }
public IList<MyOtherObject> Items { get; set; }
}
static class Program
{
static readonly RuntimeTypeModel fatModel, liteModel;
static Program()
{
// configure models
fatModel = TypeModel.Create();
fatModel.Add(typeof(MyOtherObject), false);
fatModel.Add(typeof(MyObject), false).Add("Name", "Value", "Items");
liteModel = TypeModel.Create();
liteModel.Add(typeof(MyOtherObject), false);
liteModel.Add(typeof(MyObject), false).Add("Name", "Value");
}
static void Main()
{
var obj = new MyObject
{
Name = "abc",
Value = 123,
Items = new List<MyOtherObject>
{
new MyOtherObject(),
new MyOtherObject(),
new MyOtherObject(),
new MyOtherObject(),
}
};
using (var file = File.Create("foo.bin"))
{
fatModel.Serialize(file, obj);
}
MyObject lite;
using (var file = File.OpenRead("foo.bin"))
{
lite = (MyObject)liteModel.Deserialize(
file, null, typeof(MyObject));
}
}
}
How about putting the Name and Valueinto a superclass and serializing them separately?
Alternatively, you could maintain a Dictionary and serialize that into one file.

Problem with serializing a dictionary wrapper

I defined two classes. First one...
[Serializable]
public class LocalizationEntry
{
public LocalizationEntry()
{
this.CatalogName = string.Empty;
this.Identifier = string.Empty;
this.Translation = new Dictionary<string, string>();
this.TranslationsList = new List<Translation>();
}
public string CatalogName
{
get;
set;
}
public string Identifier
{
get;
set;
}
[XmlIgnore]
public Dictionary<string, string> Translation
{
get;
set;
}
[XmlArray(ElementName = "Translations")]
public List<Translation> TranslationsList
{
get
{
var list = new List<Translation>();
foreach (var item in this.Translation)
{
list.Add(new Translation(item.Key, item.Value));
}
return list;
}
set
{
foreach (var item in value)
{
this.Translation.Add(item.Language, item.Text);
}
}
}
}
...where public List<Translation> TranslationsList is a wrapper for non-serializable public Dictionary<string, string> Translation.
Pair of key and value is defined as follows:
[Serializable]
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language
{
get;
set;
}
[XmlText]
public string Text
{
get;
set;
}
public Translation()
{
}
public Translation(string language, string translation)
{
this.Language = language;
this.Text = translation;
}
}
At last code used to serialize:
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
};
entry.Translation.Add("PL", "jabłko");
entry.Translation.Add("EN", "apple");
entry.Translation.Add("DE", "apfel");
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
}
LocalizationEntry deserializedEntry;
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
}
}
The problem is that after deserialization deserializedEntry.TranslationsList is empty. I set a breakpoint at setter of LocalizationEntry.TransalionsList and it comes from deserializer empty as well. Product of serialization is of course valid. Is there any gap in my code?
EDIT:
Here is generated XML:
<?xml version="1.0"?>
<LocalizationEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CatalogName>Catalog</CatalogName>
<Identifier>Id</Identifier>
<Translations>
<Translation lang="PL">jabłko</Translation>
<Translation lang="EN">apple</Translation>
<Translation lang="DE">apfel</Translation>
</Translations>
</LocalizationEntry>
The problem is that your TranslationList property is not being set by the Xml Deserializer. The set method will be hit but only by the call to this.TranslationsList = new List(); in the LocalisationEntry constructor. I'm not yet sure why but I suspect it's because it doesn't know how to convert an array of Translation objects back into a List.
I added the following code and it worked fine:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
I am guessing the problem has to do with this:
public List<Translation> TranslationsList
The get/set operators are designed only for something to get or assign a fully-formed list. If you tried to use this in your own code, for example, every time you would do something like
TranslationsList.Add(item)
It would just create a new list from the existing dictionary and not actually deal with your item. I bet the deserializer works much the same way: uses set to create the new object once, then uses get as it adds each item from the XML. Since all that happens in get is it copies from the dictionary (which is empty when you begin your deserialization) you end up with nothing.
Try replacing this with just a field:
public List<Translation> TranslationsList;
and then explicitly call the code to copy the dictionary to this list before you serialize, and copy it from this list to the dictionary after you deserialize. Assuming that works, you can probably figure out a more seamless way to implement what you're trying to do.
I've created a sample, which will allow you to avoid the unnecessary hidden property when using the XmlSerializer:
class Program
{
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
Translations =
{
{ "PL", "jabłko" },
{ "EN", "apple" },
{ "DE", "apfel" }
}
};
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
LocalizationEntry deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
serializer.Serialize(Console.Out, deserializedEntry);
}
}
}
public class LocalizationEntry
{
public LocalizationEntry() { this.Translations = new TranslationCollection(); }
public string CatalogName { get; set; }
public string Identifier { get; set; }
[XmlArrayItem]
public TranslationCollection Translations { get; private set; }
}
public class TranslationCollection
: Collection<Translation>
{
public TranslationCollection(params Translation[] items)
{
if (null != items)
{
foreach (Translation item in items)
{
this.Add(item);
}
}
}
public void Add(string language, string text)
{
this.Add(new Translation
{
Language = language,
Text = text
});
}
}
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language { get; set; }
[XmlText]
public string Text { get; set; }
}
There are some drawbacks when working with the XmlSerializer class itself. The .NET guidelines encourage you the not provide public-setters for collection-properties (like your translation list). But when you look at the code generated by the XmlSerializer, you'll see that it will use the Setter regardless of it is accessible. This results in a compile-error when the interim class is dynamically loaded by the XmlSerializer. The only way to avoid this, is to make the XmlSerializer think, that it can't actually create an instance of the list and thus won't try to call set for it. If the XmlSerializer detects that it can't create an instance it will throw an exception instead of using the Setter and the interim class is compiled successfully. I've used the param-keyword to trick the serializer into thinking that there is no default-constructor.
The only drawback from this solution is that you have to use a non-generic, non-interface type for the property (TranslationCollection) in my example.

Categories

Resources