[RavenDB] Polymorphic list

281 views
Skip to first unread message

Tobi

unread,
May 21, 2010, 8:34:58 AM5/21/10
to ravendb
Hi!

I can store a polymorphic list, but I can't load it it will only try to
load the base class.
Is there a trick, or isn't this possible at all?

using (var session = documentStore.OpenSession())
{
var sale = new Sale();
sale.Items.Add(new ProductSaleItem { Amount = 1.99m, ProductNumber
= "123" });
sale.Items.Add(new DiscountSaleItem { Amount = -0.10m, DiscountText
= "Hanukkah Discount" });
session.Store(sale);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
// This fails because it tries to create an instance of the
abstract SaleItem class
var sale = session.Load<Sale>("sales/1");
}

public class Sale
{
public Sale()
{
Items = new List<SaleItem>();
}
public string Id { get; set; }
public List<SaleItem> Items { get; private set; }
}

public abstract class SaleItem
{
public decimal Amount { get; set; }
}

public class ProductSaleItem : SaleItem
{
public string ProductNumber { get; set; }
}

public class DiscountSaleItem : SaleItem
{
public string DiscountText { get; set; }
}

Tobias

Ayende Rahien

unread,
May 21, 2010, 11:40:08 AM5/21/10
to rav...@googlegroups.com
Hm, this isn't a Raven issue, but a JSON.NET issue.
Yes, I know it isn't very helpful in this regard.
Quick solution would probably be to look at the JsonContractResolver, I'll need to spend some time thinking on how to solve it.

Tobi

unread,
May 21, 2010, 12:12:11 PM5/21/10
to rav...@googlegroups.com
Am 21.05.2010 17:40, schrieb Ayende Rahien:

> Hm, this isn't a Raven issue, but a JSON.NET <http://JSON.NET> issue.
> Yes, I know it isn't very helpful in this regard.
> Quick solution would probably be to look at the JsonContractResolver,
> I'll need to spend some time thinking on how to solve it.

Take your time!

Would be nice to have this working.

Tobias

Ayende Rahien

unread,
May 22, 2010, 6:29:26 AM5/22/10
to rav...@googlegroups.com
Okay, basically it is the same as I said in the beginning, you need to use the contract resolver to record that information, and read it back when you deserialize.

Emil Cardell

unread,
May 23, 2010, 9:21:14 AM5/23/10
to rav...@googlegroups.com
Even though this is a JSON problem. An example of this would be greatly appreciated.

/Emil

Ayende Rahien

unread,
May 25, 2010, 8:29:05 AM5/25/10
to rav...@googlegroups.com
Sure, here it is:

public class RecordClrTypeInJsonContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (IsValidType(objectType))
return new JsonObjectContract(objectType)
{
Converter = new RecordClrTypeInJsonConverter()
};
return base.CreateContract(objectType);
}

private static bool IsValidType(Type objectType)
{
return objectType.IsGenericType &&
typeof(List<>) == (objectType.GetGenericTypeDefinition()) &&
objectType.GetGenericArguments()[0].IsAbstract;
}

public class RecordClrTypeInJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var item in (IEnumerable)value)
{
writer.WriteStartObject();

writer.WritePropertyName("CrlType");
writer.WriteValue(item.GetType().AssemblyQualifiedName);


writer.WritePropertyName("Value");

serializer.Serialize(writer, item);

writer.WriteEndObject();
}
writer.WriteEndArray();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var list = (IList) Activator.CreateInstance(objectType);

while(reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
break;

reader.Read();//CrlType prop name
reader.Read();//actual type

var type = Type.GetType((string)reader.Value);
reader.Read();// value property
reader.Read();// actual value
var item = serializer.Deserialize(reader, type);
list.Add(item);
reader.Read(); // end object
}
return list;
}

public override bool CanConvert(Type objectType)
{
return IsValidType(objectType);

Tobi

unread,
May 26, 2010, 7:48:27 AM5/26/10
to rav...@googlegroups.com
> public class RecordClrTypeInJsonContractResolver : DefaultContractResolver

Thx, this works just fine. An older revision wasn't using the contract
resolver for serialization (only for deserialization), but with the
latest commit I just have to do:

documentStore.Conventions.JsonContractResolver = new
RecordClrTypeInJsonContractResolver();

Tobias

Reply all
Reply to author
Forward
0 new messages