Apply Setter on XML Deserialize C# [duplicate] - c#

I am trying to do a very simple bit of serialization with XmlSerializer:
public struct XmlPerson
{
[XmlAttribute] public string Id { get; set; }
[XmlAttribute] public string Name { get; set; }
}
public class GroupOfPeople
{
private Dictionary<string, string> _namesById = new Dictionary<string, string>();
//pseudo property for serialising dictionary to/from XML
public List<XmlPerson> _XmlPeople
{
get
{
var people = new List<XmlPerson>();
foreach (KeyValuePair<string, string> pair in _namesById )
people.Add(new XmlPerson() { Id = pair.Key, Name = pair.Value });
return people;
}
set
{
_namesById.Clear();
foreach (var person in value)
_namesById.Add(person.Id, person.Name);
}
}
}
Saving this class works fine, and I get:
<?xml version="1.0" encoding="utf-8"?>
<GroupOfPeople xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<_XmlPeople>
<XmlPerson Id="person1" Name="Fred" />
<XmlPerson Id="person2" Name="Bill" />
<XmlPerson Id="person3" Name="Andy" />
<XmlPerson Id="person4" Name="Nagesh" />
</_XmlPeople>
</GroupOfPeople>
However, when I read in the file again, my _XmlPeople property setter is never called, and thus the dictionary is empty. All other properties on this object get deserialized fine.
Am I missing something obvious? I have tried various collection types but none of them deserialize.
EDIT: Read code:
try
{
using (var stream = new StreamReader(itemPath))
{
var xml = new XmlSerializer(typeof(GroupOfPeople));
GroupOfPeople item = (GroupOfPeople)xml.Deserialize(stream);
}
}
//snip error stuff

Answer for clarity:
Have done some debugging and found that XmlSerializer does not call the setter for a collection.
Instead is calls the getter, and then adds items to the collection returned. Thus a solution such as Felipe's is necessary.

Have you tried using the XmlArray attribute?
With your example it would be something like this:
[XmlArray]
[XmlArrayItem(ElementName="XmlPerson")]
public List<XmlPerson> XmlPeople
EDIT:
Here, try the following structure:
public struct XmlPerson
{
[XmlAttribute] public string Id { get; set; }
[XmlAttribute] public string Name { get; set; }
}
public class GroupOfPeople
{
[XmlArray]
[XmlArrayItem(ElementName="XmlPerson")]
public List<XmlPerson> XmlPeople { get; set; }
}
I don't think it will be easy to add code to the Setter of the list, so what about getting that Dictionary when you actually need it?
Like this:
private Dictionary<string, string> _namesById;
public Dictionary<string, string> NamesById
{
set { _namesById = value; }
get
{
if (_namesById == null)
{
_namesById = new Dictionary<string, string>();
foreach (var person in XmlPeople)
{
_namesById.Add(person.Id, person.Name);
}
}
return _namesById;
}
}
This way you'll get the items from the XML and will also mantain that Dictionary of yours.

In this question, the _XmlPeople property serves as a "proxy" for the _namesById dictionary, which is where the collection elements are actually stored. Its getters and setters convert between different types of collections.
This does not work with deserialization, and the reason is pointed out in https://stackoverflow.com/a/10283576/2279059.
(Deserialization calls the getter, then adds elements to the "temporary" collection it returns, which is then discarded).
A generic solution is to implement the "proxy collection" as a separate class, then have a property which is a proxy collection:
(In the code below, the "actual collection" is _nameById, and MyItem is XmlPerson)
public class MyProxyCollection : IList<MyItem> {
MyProxyCollection(... /* reference to actual collection */ ...) {...}
// Implement IList here
}
public class MyModel {
MyProxyCollection _proxy;
public MyModel() {
_proxy = new MyProxyCollection (... /* reference to actual collection */ ...);
}
// Here we make sure the getter and setter always return a reference to the same
// collection object. This ensures that we add items to the correct collection on
// deserialization.
public MyProxyCollection Items {get; set;}
}
This ensures that getting/setting the collection object works as expected.

Related

Json.NET deserialize array into property

Is there a way to deserialize a JSON array directly into the property of an object?
I have a JSON consisting of an array of Entry objects. To get them into a Collection class I could go the route of:
var coll = new Collection();
coll.Entries = JsonConvert.DeserializeObject<List<Entry>>(json);
Is there a way to define Collection in a way to skip this step an directly call var coll = JsonConvert.DeserializeObject<Collection>(json)
You could let your Collection class implement IEnumerable<Entry> and decorate it with the JsonObject attribute. This should give Json.NET the ability to deserialize your class correctly.
[JsonObject]
public class Collection : IEnumerable<Entry>
{
public IEnumerable<Entry> Entries { get; };
public GetEnumerator() {
return Entries.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
While silkfires answer didn't work for me, it gave me a hint for the solution:
I added the [JsonArray] attribute and implemented ICollection<Entry> by pointing all methods to the Entries list.
[JsonArray]
public class Collection: ICollection<Entry>
{
public LinkedList<Entry> Entries { get; } = new LinkedList<Entry>();
public void Add(Entry item)
{
Entries.AddLast(item);
}
...

How to store multiple items in IDictionary?

I have this IDictionary declaration: IDictionary<string, string> trace;
Inside of it I want save a list of element returned by a json deserialization, actually I do this like:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
trace["date"] = item.trace.details.date;
trace["type"] = item.trace.details.type;
foreach (var trace in item.trace.context.context)
{
trace["context"] = JsonConvert.SerializeObject(trace);
}
}
Now this code working good but save only the last item of the iteration, this 'cause when I iterate over the item the date, type etc... are replaced, but I want store the item not replace it in each new iteration.
How can do this with IDictionary?
Note that: trace.x is the name of the class that deserialize the json.
Further question: Maybe the use of IDictionary is not a good idea for achieve this?
Looks like you'd be better off using a dedicated class, something like this:
public class TraceInfo
{
public string Date { get; set; }
public string Type { get; set; }
public List<string> ContextItems { get; set; }
}
Then for every value in obj, create a TraceInfo object using new TraceInfo() and set its properties.
You can then store them in a List<TraceInfo> or in a Dictionary<string, TraceInfo>, the choice is yours.

Setter not called when deserializing collection

I am trying to do a very simple bit of serialization with XmlSerializer:
public struct XmlPerson
{
[XmlAttribute] public string Id { get; set; }
[XmlAttribute] public string Name { get; set; }
}
public class GroupOfPeople
{
private Dictionary<string, string> _namesById = new Dictionary<string, string>();
//pseudo property for serialising dictionary to/from XML
public List<XmlPerson> _XmlPeople
{
get
{
var people = new List<XmlPerson>();
foreach (KeyValuePair<string, string> pair in _namesById )
people.Add(new XmlPerson() { Id = pair.Key, Name = pair.Value });
return people;
}
set
{
_namesById.Clear();
foreach (var person in value)
_namesById.Add(person.Id, person.Name);
}
}
}
Saving this class works fine, and I get:
<?xml version="1.0" encoding="utf-8"?>
<GroupOfPeople xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<_XmlPeople>
<XmlPerson Id="person1" Name="Fred" />
<XmlPerson Id="person2" Name="Bill" />
<XmlPerson Id="person3" Name="Andy" />
<XmlPerson Id="person4" Name="Nagesh" />
</_XmlPeople>
</GroupOfPeople>
However, when I read in the file again, my _XmlPeople property setter is never called, and thus the dictionary is empty. All other properties on this object get deserialized fine.
Am I missing something obvious? I have tried various collection types but none of them deserialize.
EDIT: Read code:
try
{
using (var stream = new StreamReader(itemPath))
{
var xml = new XmlSerializer(typeof(GroupOfPeople));
GroupOfPeople item = (GroupOfPeople)xml.Deserialize(stream);
}
}
//snip error stuff
Answer for clarity:
Have done some debugging and found that XmlSerializer does not call the setter for a collection.
Instead is calls the getter, and then adds items to the collection returned. Thus a solution such as Felipe's is necessary.
Have you tried using the XmlArray attribute?
With your example it would be something like this:
[XmlArray]
[XmlArrayItem(ElementName="XmlPerson")]
public List<XmlPerson> XmlPeople
EDIT:
Here, try the following structure:
public struct XmlPerson
{
[XmlAttribute] public string Id { get; set; }
[XmlAttribute] public string Name { get; set; }
}
public class GroupOfPeople
{
[XmlArray]
[XmlArrayItem(ElementName="XmlPerson")]
public List<XmlPerson> XmlPeople { get; set; }
}
I don't think it will be easy to add code to the Setter of the list, so what about getting that Dictionary when you actually need it?
Like this:
private Dictionary<string, string> _namesById;
public Dictionary<string, string> NamesById
{
set { _namesById = value; }
get
{
if (_namesById == null)
{
_namesById = new Dictionary<string, string>();
foreach (var person in XmlPeople)
{
_namesById.Add(person.Id, person.Name);
}
}
return _namesById;
}
}
This way you'll get the items from the XML and will also mantain that Dictionary of yours.
In this question, the _XmlPeople property serves as a "proxy" for the _namesById dictionary, which is where the collection elements are actually stored. Its getters and setters convert between different types of collections.
This does not work with deserialization, and the reason is pointed out in https://stackoverflow.com/a/10283576/2279059.
(Deserialization calls the getter, then adds elements to the "temporary" collection it returns, which is then discarded).
A generic solution is to implement the "proxy collection" as a separate class, then have a property which is a proxy collection:
(In the code below, the "actual collection" is _nameById, and MyItem is XmlPerson)
public class MyProxyCollection : IList<MyItem> {
MyProxyCollection(... /* reference to actual collection */ ...) {...}
// Implement IList here
}
public class MyModel {
MyProxyCollection _proxy;
public MyModel() {
_proxy = new MyProxyCollection (... /* reference to actual collection */ ...);
}
// Here we make sure the getter and setter always return a reference to the same
// collection object. This ensures that we add items to the correct collection on
// deserialization.
public MyProxyCollection Items {get; set;}
}
This ensures that getting/setting the collection object works as expected.

How to create a property for a List<T>

private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
I want to create something like this, but this is won't work. it's just an example to demonstrate my goal as it's pretty common creating proprties for string and int and even T but I've never seen a List property
Is it even possible do such a thing, creating a property for type List ?
EDIT
I have a normal class that has normal properties (string properties, int properties, etc) but I have this property that stores user options, So on the presentation layer I had to convert them into a string so I could be able to store them in the Object. Now is it possible to have a property of type List to store the multivalues in a better and clean way, instead of converting information into one string and then split it and again join it! Thanks Again =D
EDIT2
private List<KeyValuePair<string, string>> _settings;
public List<KeyValuePair<string, string>> MySettings
{
get { return _settings; }
set { _settings = value; }
}
I used the exact code you posted but the property still won't appear in the object's instance, so I tried adding code in the get and set (I wonder why you left them empty or does it means something?) and also added a private variable in the class but still it doesn't appear in the properties of the object's instance!
I hope you could provide the exact code to implement this property and a simple code that assigns or retrieves from/to an instance of this class object
It's the first time to even hear about this KeyValuePair and all the tutorials are pretty simple and not for my case, sorry!
The Last Edit: After a lot of researching and the help of Mark Avenius I found the perfect answer. hope everyone can benefit from this.
NOW! HOW TO CREATE A PROPERTY FOR A LIST :
The Options Class
Public Class Options
{
private string id;
private int option;
public int ID
{
get { return id; }
set { id= value; }
}
public string Option
{
get { return option; }
set { option = value; }
}
}
The Users Class
public class Users
{
private int userId;
private string pass;
private List<Options> userOptions = new List<Options>();
public int ID
{
get { return userId; }
set { user = userId; }
}
public string Pass
{
get { return pass; }
set { pass = value; }
}
public List<Options> OptionsList
{
get { return userOptions; }
set { userOptions = value; }
}
}
The Presentation Layer
Users newUser = new Users ();
Options userOption = new Options ();
userOption.ID = int.Parse(txtBxID.Text);
userOption.Option = txtBxOption.Text;
Item.Options.Add(userOption);
T must be defined within the scope in which you are working. Therefore, what you have posted will work if your class is generic on T:
public class MyClass<T>
{
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
}
Otherwise, you have to use a defined type.
EDIT: Per #lKashef's request, following is how to have a List property:
private List<int> newList;
public List<int> NewList
{
get{return newList;}
set{newList = value;}
}
This can go within a non-generic class.
Edit 2:
In response to your second question (in your edit), I would not recommend using a list for this type of data handling (if I am understanding you correctly). I would put the user settings in their own class (or struct, if you wish) and have a property of this type on your original class:
public class UserSettings
{
string FirstName { get; set; }
string LastName { get; set; }
// etc.
}
public class MyClass
{
string MyClassProperty1 { get; set; }
// etc.
UserSettings MySettings { get; set; }
}
This way, you have named properties that you can reference instead of an arbitrary index in a list. For example, you can reference MySettings.FirstName as opposed to MySettingsList[0].
Let me know if you have any further questions.
EDIT 3:
For the question in the comments, your property would be like this:
public class MyClass
{
public List<KeyValuePair<string, string>> MySettings { get; set; }
}
EDIT 4: Based on the question's edit 2, following is how I would use this:
public class MyClass
{
// note that this type of property declaration is called an "Automatic Property" and
// it means the same thing as you had written (the private backing variable is used behind the scenes, but you don't see it)
public List<KeyValuePair<string, string> MySettings { get; set; }
}
public class MyConsumingClass
{
public void MyMethod
{
MyClass myClass = new MyClass();
myClass.MySettings = new List<KeyValuePair<string, string>>();
myClass.MySettings.Add(new KeyValuePair<string, string>("SomeKeyValue", "SomeValue"));
// etc.
}
}
You mentioned that "the property still won't appear in the object's instance," and I am not sure what you mean. Does this property not appear in IntelliSense? Are you sure that you have created an instance of MyClass (like myClass.MySettings above), or are you trying to access it like a static property (like MyClass.MySettings)?
Simple and effective alternative:
public class ClassName
{
public List<dynamic> MyProperty { get; set; }
}
or
public class ClassName
{
public List<object> MyProperty { get; set; }
}
For differences see this post: List<Object> vs List<dynamic>
public class MyClass<T>
{
private List<T> list;
public List<T> MyList { get { return list; } set { list = value; } }
}
Then you can do something like
MyClass<int> instance1 = new MyClass<int>();
List<int> integers = instance1.MyList;
MyClass<Person> instance2 = new MyClass<Person>();
IEnumerable<Person> persons = instance2.MyList;
You could do this but the T generic parameter needs to be declared at the containing class:
public class Foo<T>
{
public List<T> NewList { get; set; }
}
It's possible to have a property of type List<T> but your class needs to be passed the T too.
public class ClassName<T>
{
public List<T> MyProperty { get; set; }
}
Either specify the type of T, or if you want to make it generic, you'll need to make the parent class generic.
public class MyClass<T>
{
etc

Serializing object that inherits from List<T>

When I try to serialize this collection, the name property is not serialized.
public class BCollection<T> : List<T> where T : B_Button
{
public string Name { get; set; }
}
BCollection<BB_Button> bc = new BCollection<B_Button>();
bc.Name = "Name";// Not Serialized!
bc.Add(new BB_Button { ID = "id1", Text = "sometext" });
JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(bc);
Only if I create a new class (without List<t> inheritance), and define there string Name property and List<B_Button> bc = new List<B_Button>(); property I get the right result.
In many serializers (and data-binding, in fact), an object is either an entity or (exclusive) a list; having properties on a list is not commonly supported. I would refactor to encapsulate the list:
public class Foo<T> {
public string Name {get;set;}
private readonly List<T> items = new List<T>();
public List<T> Items { get { return items; } }
}
Also; how would you plan on representing that in JSON? IIRC the JSON array syntax doesn't allow for extra properties either.

Categories