I have a class which I use the XmlSerializer with to serialize data to and from XML files.
I have several DateTime properties. In the post, Prevent timezone conversion on deserialization of DateTime value the answer correctly removes timezone offsets from DateTime properties.
However, I have a property which is a list of DateTime objects that I can't remove the timezones from.
[XmlElement]
public List<DateTime> Times {get; set;}
I have tired something like this, but the value is always null and none of the data is correctly serialized to the list property.
[XmlIgnore]
public List<DateTime> Times {get; set;}
[XmlElement(ElementName = "Times")]
public List<string> TimesString
{
get
{
return Times.ForEach(fe => RemoveTimeZone(fe));
}
set
{
foreach(var v in value)
{
Times.Add(ConvertToDate(v));
}
}
}
The value property is always empty and both list properties are always empty.
My goal is to not create a new class, but to somehow bind directly to my list properties.
Your TimesString property is a proxy collection property, i.e. a property that gets or sets an underlying collection, transforming its members in the process. The simplest way to make such a proxy collection work correctly with XmlSerializer is to make it be an array rather than a list, in your case a string []:
[XmlIgnore]
public List<DateTime> Times { get; set; }
[XmlElement(ElementName = "Times")]
public string [] TimesString
{
get
{
return Times == null ? null : Times.Select(t => RemoveTimeZone(t)).ToArray();
}
set
{
if (value == null)
return;
(Times = Times ?? new List<DateTime>(value.Length)).AddRange(value.Select(s => ConvertToDate(s)));
}
}
string [] works while List<string> does not because XmlSerializer deserializes a property referring to a class implementing IList<T> in the following way:
It calls the getter to get the list. If null, it allocates a list and sets it via the setter. It holds onto the list in some local variable while populating it.
It deserializes each list element, and adds it to the list it is holding.
And that's it. It never calls the containing class's list property setter afterwards.
However, since an array is basically a read-only collection and so cannot be added to once allocated, it cannot be set back until completely populated. This is what XmlSerializer does, which allows proxy array properties to deserialized successfully.
For more, see Cannot deserialize XML into a list using XML Deserializer or XML Deserialization of collection property with code defaults.
Related
In my code I want to use ShouldDeserialize method to make my response more cleaner, but ShouldDeserialize{Property} method is not visible for deserialize method. In below code ShouldSerializeItems predicate works.
public class ItemsContainer
{
public string Id { get; set; }
[JsonProperty]
public IEnumerable<Item> Items{ get; set; }
//Working
public bool ShouldSerializeItems()
{
return !Items.All(x =>
string.IsNullOrEmpty(x.ItemName) && string.IsNullOrEmpty(x.ItemId));
}
// Not working
public bool ShouldDeserializeItems()
{
return !Items.All(x =>
string.IsNullOrEmpty(x.ItemName) && string.IsNullOrEmpty(x.ItemId));
}
}
And call deserialize:
JsonConvert.DeserializeObject<ItemsContainer>(json);
In newtonsoft documentation both serialize and deserialize predicate are documented:
https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_JsonProperty_ShouldDeserialize.htm
https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_JsonProperty_ShouldSerialize.htm
Checking for a property named named ShouldDeserialize*() and automatically calling it was never implemented by Json.NET. See: Conditional serialization/deserialization of object properties with json.net. The infrastructure is there, so you could do it yourself by implementing a custom contract resolver that overrides DefaultContractResolver.CreateProperty() and sets JsonProperty.ShouldDeserialize.
That being said, it appears that your method ShouldDeserializeItems() assumes that the Items have already been deserialized, then filters them out based on some custom logic. Json.NET's JsonProperty.ShouldDeserialize predicate is called before deserialization, not after. When it returns true the JSON property is skipped with no ability to examine the contents, and thus cannot be used here.
Instead, you should use an [OnDeserialized] callback to clear out unwanted items after deserialization:
[System.Runtime.Serialization.OnDeserialized]
private void OnDeserializedMethod(System.Runtime.Serialization.StreamingContext context)
{
if (Items != null && Items.All(x =>
string.IsNullOrEmpty(x.ItemName) && string.IsNullOrEmpty(x.ItemId)))
Items = new List<Item>();
}
But when you are about to deserialize the Items property of the ItemContainer there is likely nothing in the Items property yet. You may not need this predicate because if the ShouldSerialize is working correctly you will never have junk data to deserialize.
My question is similar to this question - JSON.NET deserialize a specific property
. Except I want to deserialize the whole object but also use a custom method to deserialize a specific property in the class. The way I'm doing it right now is deserializing the whole object, and then calling a custom method again to deserialize a specific property in that object. This works, but I don't think it is very efficient.
public class Foo
{
public int id { get; set; }
public object item { get; set; }
}
object obj = JsonConvert.DeserializeObject(json, typeof(Foo));
//This method is from the previously asked question and it works.
object item = GetFirstInstance<GenericFoo<object>>("item", json);
Foo castedFoo= (Foo)obj;
castedFoo.item = item;
How can I update this code to make it more efficient? Something like ignore item property when the first time the object is getting deserialized?
I have a property
[XmlElement]
public string[] Emails { get; set; }
which is initialized as string[0] at constructor.
If I XML serialize and deserialize a default instance of this object, the property is NULL.
How can I tell the XML Serializer to use an empty array instead of NULL for this property?
5 years later... :)
Replacing Array with List<> did the trick for me.
[XmlElement (IsNullable = false)]
public List<string> Emails {get;set;}
I want to serialize a MyClass, which is a class that contains a list MyClass.
In the XML, I want to write only myClass.Name, then when I deserialize it, I then find which MyClass should be in which other MyClass. I have the following code that properly serializes the list of MyClass into a list of string. However, it doesn't deserialize the list of string.
//List of actual object. It's what I use when I work with the object.
[XmlIgnore]
public List<TaskConfiguration> ChildTasks { get; set; }
//Used by the serializer to get the string list, and used
//by the serializer to deserialize the string list to.
[XmlArray("ChildTasks")]
public List<string> ChildTasksSurrogate
{
get
{
List<string> childTaskList = new List<string>();
if (ChildTasks != null)
childTaskList.AddRange(ChildTasks.Select(ct => ct.Name).ToList());
if (_childTasksSurrogate != null)
childTaskList.AddRange(_childTasksSurrogate);
//Clears it not to use it when it serializes.
_childTasksSurrogate = null;
return childTaskList;
}
set
{
_childTasksSurrogate = value;
}
}
[XmlIgnore]
private List<string> _childTasksSurrogate;
As I said, the serialization works. The problem lies with the deserialization. After the deserialization, MyClass._childTasksSurrogate is null.
The problem was related to HOW does the XmlSerializer deserializes the Xml :
I thought that the XmlSerializer would assign the whole property (read: myList = DeserializedList), while it looks like it adds all the elements (read: myList.AddRange(DeserializedList).
I have a list of objects that implement a common interface. If I try to simply serialize it I get a nice exception that tells me that the serializer cannot serialize interfaces:
private readonly ObservableCollection<ICanHasInterface> children = new ObservableCollection<ICanHasInterface>();
public ObservableCollection<ICanHasInterface> Children
{
get { return children; }
}
=> "Cannot serialize member ... of type ... because it is an interface"
Apparently asking the serializer to get the type of the objects and mark the XmlElement with the attribute xsi:type (which is done if an object inherits from another class) is too much.
So because I do not want to implement IXmlSerializable, I thought up a workaround which looked promising initially:
private readonly ObservableCollection<ICanHasInterface> children = new ObservableCollection<ICanHasInterface>();
[XmlIgnore()]
public ObservableCollection<ICanHasInterface> Children
{
get { return children; }
}
[XmlElement("Child")]
public List<object> ChildrenSerialized
{
get
{
return new List<object>(Children);
}
set
{
Children.Clear();
foreach (var child in value)
{
if (child is ICanHasInterface) AddChild(child as ICanHasInterface);
}
}
}
With this at least the serialisation works just fine (Note: Either specify XmlInclude attributes for the types that can be in the original list or hand over an array of types in the constructor of the serializer), however if the object is deserialized the Children collection ends up empty because the set block is never reached during deserialization, I am quite clueless as to why this is; any ideas?
On deserialization the serializer uses your property getter to get the collection instance and then calls Add() on it for each item. It does not call your property setter. Something like this:
YourClass c = new YourClass();
c.ChildrenSerialized.Add(ReadValue());
...
In order to keep the collections synchronized you'd need to customize the Add() behavior of the collection you return from the property getter.
A better option is to change the ChildrenSerialized property to use an object[]. For arrays, the serializer reads the value into an array and then calls your property setter with the value.