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.
Related
I have a HashSet as seen below:
public name {get; set; }
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace, TypeNameHandling = TypeNameHandling.Auto)]
public ICollection<T> data { get; set; }
public MyClass(string name)
{
name = name;
data = new HashSet<T>(new CustomComparer());
}
The comparer looks like:
public class CustomComparer: EqualityComparer<T>
{
public override bool Equals(T x, T y)
{
return string.Equals(x.val, y.val, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(T obj)
{
return $"{obj?.val?.ToLowerInvariant()}-{obj?.val?.ToLowerInvariant()}".GetHashCode();
}
}
Now, I have a unit test where I first create an instance of MyClass as:
var obj1 = new MyClass("test");
obj1.data.Add(CustomObject1);
obj1.data.Add(CustomObject2);
The next step I do is serialize and deserialize the object.
var jsonSerializerSettings = new JsonSerializerSettings();
var serializedObject = JsonConvert.SerializeObject(obj1, jsonSerializerSettings);
var deserializedUserObject = JsonConvert.DeserializeObject<MyClass>(serializedObject, jsonSerializerSettings);
Now, when I try to add CustomObject1 back to the deserialized object (which is already present), it still adds to the set.
Not sure why this is happening.
Any leads would be helpful.
This passed when I created a default constructor and initialized the data parameter as below.
public MyClass()
{
data = new HashSet<T>(new CustomComparer());
}
Not sure if this is the correct way to achieve this.
As there will be multiple instances of the data object being created unnecessarily.
Is there any better way to get this done?
Imposing a [JsonConstructor] property on top of the parameterized constructor didn't work too.
NOTE: I removed the ObjectCreationHandling property from the JsonProperty attribute.
I'm looking at ways to introduce something other than BinaryFormatter serialization into my app to eventually work with Redis. ServiceStack JSON is what I would like to use, but can it do what I need with interfaces?
It can serialize (by inserting custom __type attribute)
public IAsset Content;
but not
public List<IAsset> Contents;
- the list comes up empty in serialized data. Is there any way to do this - serialize a list of interface types?
The app is big and old and the shape of objects it uses is probably not going to be allowed to change.
Thanks
Quoting from http://www.servicestack.net/docs/framework/release-notes
You probably don't have to do much :)
The JSON and JSV Text serializers now support serializing and
deserializing DTOs with Interface / Abstract or object types. Amongst
other things, this allows you to have an IInterface property which
when serialized will include its concrete type information in a __type
property field (similar to other JSON serializers) which when
serialized populates an instance of that concrete type (provided it
exists).
[...]
Note: This feature is automatically added to all
Abstract/Interface/Object types, i.e. you don't need to include any
[KnownType] attributes to take advantage of it.
By not much:
public interface IAsset
{
string Bling { get; set; }
}
public class AAsset : IAsset
{
public string Bling { get; set; }
public override string ToString()
{
return "A" + Bling;
}
}
public class BAsset : IAsset
{
public string Bling { get; set; }
public override string ToString()
{
return "B" + Bling;
}
}
public class AssetBag
{
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public List<IAsset> Assets { get; set; }
}
class Program
{
static void Main(string[] args)
{
try
{
var bag = new AssetBag
{
Assets = new List<IAsset> {new AAsset {Bling = "Oho"}, new BAsset() {Bling = "Aha"}}
};
string json = JsonConvert.SerializeObject(bag, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
});
var anotherBag = JsonConvert.DeserializeObject<AssetBag>(json, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
});
I am trying to dynamically serialize a List to Xml.
I am able to do so, as long I do not have a ICollection as a property of T.
I would like to dynamically overwrite the ICollection type into List before I write it to Xml.
This is what I have so far.
List<-XmlElementAttribute-> attrToConvertList = new List<-XmlElementAttribute->();
foreach (var propertyInfo in typeof(T).GetProperties())
{
if (propertyInfo.PropertyType.Name == "ICollection`1")
{
XmlElementAttribute attrToConvert = new XmlElementAttribute();
attrToConvert.ElementName = propertyInfo.Name;
attrToConvert.Type = typeof(List<>);
attrToConvert.Type = attrToConvert.Type.MakeGenericType(propertyInfo.PropertyType.GetGenericArguments()[0]);
attrToConvertList.Add(attrToConvert);
}
}
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attributesToConvert = new XmlAttributes();
foreach (var xmlElementAttribute in attrToConvertList)
attributesToConvert.XmlElements.Add(xmlElementAttribute);
overrides.Add(typeof(T), attributesToConvert);
XmlSerializer serializer = new XmlSerializer(typeof(List<T>), overrides);
I get the error that I cannot serialize the type ICollection because it is an interface.
I was under the impression that what I was doing with the XmlAttributeOverrides was supposed to overwrite the ICollection to the type List.
XML serialization doesn't handle interfaces, and apparently XmlAttributeOverride doesn't allow you to bypass that behavior. You can change the type of your property, or make a type, for serialization only, where the property is a List<T>.
Example:
class RealClass
{
ICollection<int> SomeInts { get; set; }
}
class MySerializationClass
{
private readonly RealClass _wrappedObject;
public SerializationClass() : this(new RealClass()) { }
public SerializationClass(RealClass wrappedObject)
{
_wrappedObject = wrappedObject;
}
public List<T> SomeInts
{
get { return new List<T>(_wrappedObject.SomeInts); }
set { _wrappedObject.SomeInts = value; }
}
}
You could also do this with explicit interface member implementation, and use the interface in most of your code:
interface IHaveSomeInts
{
ICollection<int> SomeInts { get; set; }
}
class TheClass : IHaveSomeInts
{
public List<T> SomeInts { get; set; }
ICollection<T> IHaveSomeInts.SomeInts
{
get { return SomeInts; }
set { SomeInts = new List<T>(value); }
}
}
When assigning an ICollection<T> to an IList<T>, I would probably use as to see if I can just cast the object rather than creating a new one, to avoid creating lists needlessly.
I solved my original issue by using Newton.Json to serialize the object.
json.net (newtonsoft)
I am looking through the documentation but I can't find anything on this or the best way to do it.
public class Base
{
public string Name;
}
public class Derived : Base
{
public string Something;
}
JsonConvert.Deserialize<List<Base>>(text);
Now I have Derived objects in the serialized list. How do I deserialize the list and get back derived types?
You have to enable Type Name Handling and pass that to the (de)serializer as a settings parameter.
Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);
This will result in correct deserialization of derived classes. A drawback to it is that it will name all the objects you are using, as such it will name the list you are putting the objects in.
If you are storing the type in your text (as you should be in this scenario), you can use the JsonSerializerSettings.
See: how to deserialize JSON into IEnumerable<BaseType> with Newtonsoft JSON.NET
Be careful, though. Using anything other than TypeNameHandling = TypeNameHandling.None could open yourself up to a security vulnerability.
Since the question is so popular, it may be useful to add on what to do if you want to control the type property name and its value.
The long way is to write custom JsonConverters to handle (de)serialization by manually checking and setting the type property.
A simpler way is to use JsonSubTypes, which handles all the boilerplate via attributes:
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
Use this JsonKnownTypes, it's very similar way to use, it just add discriminator to json:
[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
public string Name;
}
public class Derived : Base
{
public string Something;
}
Now when you serialize object in json will be add "$type" with "base" and "derived" value and it will be use for deserialize
Serialized list example:
[
{"Name":"some name", "$type":"base"},
{"Name":"some name", "Something":"something", "$type":"derived"}
]
just add object in Serialize method
var jsonMessageBody = JsonSerializer.Serialize<object>(model);
I'm trying to deserialize json to an object model where the collections are represented as IList<T> types.
The actual deserializing is here:
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Deserialize<IList<Contact>>(
(new StreamReader(General.GetEmbeddedFile("Contacts.json")).ReadToEnd()));
Before i post the exception i'm getting you should know what the implicit conversions are. This is the Contact type:
public class Contact
{
public int ID { get; set; }
public string Name { get; set; }
public LazyList<ContactDetail> Details { get; set; }
//public List<ContactDetail> Details { get; set; }
}
And this is the ContactDetail type:
public class ContactDetail
{
public int ID { get; set; }
public int OrderIndex { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
The important thing to know with the LazyList<T> is that it implements IList<T>:
public class LazyList<T> : IList<T>
{
private IQueryable<T> _query = null;
private IList<T> _inner = null;
private int? _iqueryableCountCache = null;
public LazyList()
{
this._inner = new List<T>();
}
public LazyList(IList<T> inner)
{
this._inner = inner;
}
public LazyList(IQueryable<T> query)
{
if (query == null)
throw new ArgumentNullException();
this._query = query;
}
Now this LazyList<T> class definition was fine until i tried deserializing Json into it. The System.Web.Script.Serialization.JavaScriptSerializer seems to want to serialize lists to List<T> which makes sense coz of it's age but i need them in the type IList<T> so they will cast into my LazyList<T> (at least that's where i think i am going wrong).
I get this exception:
System.ArgumentException: Object of type 'System.Collections.Generic.List`1[ContactDetail]' cannot be converted to type 'LazyList`1[ContactDetail]'..
When i try using List<ContactDetail> in my Contact type (as you can see commented above) it seems to work. But i dont want to use List<T>'s. I even tried having my LazyList<T> inheriting from List<T> which seemed to execute but passing the List<T>'s internal T[] to my implementation was a nightmare and i simply don't want the bloat of List<T> anywhere in my model.
I also tried some other json libraries to no avail (it's possible i may not be using these to their full potential. I more or less replaced the references and attempted to repeat the code quoted at the top of this question. Maybe passing settings params will help??).
I dont know what to try now. Do i go with another deserializer? Do i tweak the deserializing itself? Do i need to change my types to please the deserializer? Do i need to worry more about implicit casting or just implement another interface?
It is not possible to deserialize directly to an interface, as interfaces are simply a contract. The JavaScriptSerializer has to deserialize to some concrete type that implements IList<T>, and the most logical choice is List<T>. You will have to convert the List to a LazyList, which given the code you posted, should be easy enough:
var list = serializer.Deserialize<IList<Contact>>(...);
var lazyList = new LazyList(list);
Unfortunately you will probably need to fix your class, as there is no way for a deserializer to know that it should be of type IList, since List is an implementation of IList.
Since the deserializers at http://json.org have source available you could just modify one to do what you want.
I ended up using the Json.NET lib which has good linq support for custom mapping. This is what my deserializing ended up looking like:
JArray json = JArray.Parse(
(new StreamReader(General.GetEmbeddedFile("Contacts.json")).ReadToEnd()));
IList<Contact> tempContacts = (from c in json
select new Contact
{
ID = (int)c["ID"],
Name = (string)c["Name"],
Details = new LazyList<ContactDetail>(
(
from cd in c["Details"]
select new ContactDetail
{
ID = (int)cd["ID"],
OrderIndex = (int)cd["OrderIndex"],
Name = (string)cd["Name"],
Value = (string)cd["Value"]
}
).AsQueryable()),
Updated = (DateTime)c["Updated"]
}).ToList<Contact>();
return tempContacts;