MongoDB Custom Serializer to avoid _t being added collection, throws ReadEndArray Error?

aeatonDev

Situation: Language: C# using the C# Driver I have a model that contains a List as a property. That List can contain one of 3 different models that all inherit the BaseModelClass. To assist in serialization of this situation Mongo adds _t to identify which of the models is actually being used. For us this is a problem due to the amount of space that _t is taking up. I am a lowly dev, I have asked for more space and ram and they have told me to solve it without the additional space. So I sat down to writing a custom serializer that handled the different types without writing a _t to the BSONDocument. I thought all was great until I started doing my unit testing of the serialization. I started getting "ReadEndArray can only be called when ContextType is Array, not when ContextType is Document."

Any advice or suggestions are greatly appreciated.

Here is the code I have thus far...

<---------Collection Model--------------------->

[BsonCollectionName("calls")]
[BsonIgnoreExtraElements]
public class Call
{
    [BsonId]
    public CallId _id { get; set; }

    [BsonElement("responses")]
    [BsonIgnoreIfNull]
    public IList<DataRecord> Responses { get; set; }
}

<----------Base Data Record------------------>

[BsonSerializer(typeof(DataRecordSerializer))]
public abstract class DataRecord
{
    [BsonElement("key")]
    public string Key { get; set; }
}

<-----------Examples of actual Data Records----------------->

[BsonSerializer(typeof(DataRecordSerializer))]
public class DataRecordInt : DataRecord
{
    [BsonElement("value")]
    public int Value { get; set; }
}

[BsonSerializer(typeof(DataRecordSerializer))]
public class DataRecordDateTime : DataRecord
{
    [BsonElement("value")]
    public DateTime? Value { get; set; }
}

<---------------Unit Test to trigger Deserializer----------------->

        //Arrange
        var bsonDocument = TestResources.SampleCallJson;

        //Act
        var result = BsonSerializer.Deserialize<Call>(bsonDocument);

        //Assert
        Assert.IsTrue(true);

<----------------Serializer----------------->

public class DataRecordSerializer : IBsonSerializer 
{
    public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
    {
        //Entrance Criteria
        if(nominalType != typeof(DataRecord)) throw new BsonSerializationException("Must be of base type DataRecord.");
        if(bsonReader.GetCurrentBsonType() != BsonType.Document) throw new BsonSerializationException("Must be of type Document.");

        bsonReader.ReadStartDocument();
        var key = bsonReader.ReadString("key");
        bsonReader.FindElement("value");

        var bsonType = bsonReader.CurrentBsonType;

        if (bsonType == BsonType.DateTime)
        {
            return DeserializeDataRecordDateTime(bsonReader, key);
        }

        return bsonType == BsonType.Int32 ? DeserializeDataRecordInt(bsonReader, key) : DeserializeDataRecordString(bsonReader, key);
    }

    public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
    {
        //Entrance Criteria
        if (nominalType != typeof (DataRecord)) throw new BsonSerializationException("Must be of base type DataRecord.");
        if (bsonReader.GetCurrentBsonType() != BsonType.Document) throw new BsonSerializationException("Must be of type Document.");

        bsonReader.ReadStartDocument(); // Starts Reading and is able to pull data fine through this and the next few lines of code.
        var key = bsonReader.ReadString("key");

        if (actualType == typeof(DataRecordDateTime))
        {
            return DeserializeDataRecordDateTime(bsonReader, key);
        }

        return actualType == typeof(DataRecordInt) ? DeserializeDataRecordInt(bsonReader, key) : DeserializeDataRecordString(bsonReader, key); // Once it tries to return I am getting the following Error: ReadEndArray can only be called when ContextType is Array, not when ContextType is Document.
    }

    public IBsonSerializationOptions GetDefaultSerializationOptions()
    {
        return new DocumentSerializationOptions
        {
            AllowDuplicateNames = false,
            SerializeIdFirst = false
        };
    }

    public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
    {
        var currentType = value.GetType();
        if (currentType == typeof (DataRecordInt))
        {
            SerializeDataRecordInt(bsonWriter, value);
            return;
        }

        if (currentType == typeof(DataRecordDateTime))
        {
            SerializeDataRecordDateTime(bsonWriter, value);
            return;
        }

        if (currentType == typeof(DataRecordString))
        {
            SerializeDataRecordString(bsonWriter, value);
        }
    }

    private static object DeserializeDataRecordString(BsonReader bsonReader, string key)
    {
        var stringValue = bsonReader.ReadString();
        var isCommentValue = false;
        if (bsonReader.FindElement("iscomment"))
        {
            isCommentValue = bsonReader.ReadBoolean();
        }

        return new DataRecordString
        {
            Key = key,
            Value = stringValue,
            IsComment = isCommentValue
        };
    }

    private static object DeserializeDataRecordInt(BsonReader bsonReader, string key)
    {
        var intValue = bsonReader.ReadInt32();

        return new DataRecordInt
        {
            Key = key,
            Value = intValue
        };
    }

    private static object DeserializeDataRecordDateTime(BsonReader bsonReader, string key)
    {
        var dtValue = bsonReader.ReadDateTime();
        var dateTimeValue = new BsonDateTime(dtValue).ToUniversalTime();

        return new DataRecordDateTime
        {
            Key = key,
            Value = dateTimeValue
        };
    }

    private static void SerializeDataRecordString(BsonWriter bsonWriter, object value)
    {
        var stringRecord = (DataRecordString) value;
        bsonWriter.WriteStartDocument();

        var keyValue = stringRecord.Key;
        bsonWriter.WriteString("key", string.IsNullOrEmpty(keyValue) ? string.Empty : keyValue);

        var valueValue = stringRecord.Value;
        bsonWriter.WriteString("value", string.IsNullOrEmpty(valueValue) ? string.Empty : valueValue);

        bsonWriter.WriteBoolean("iscomment", stringRecord.IsComment);
        bsonWriter.WriteEndDocument();
    }

    private static void SerializeDataRecordDateTime(BsonWriter bsonWriter, object value)
    {
        var dateRecord = (DataRecordDateTime) value;
        var millisecondsSinceEpoch = dateRecord.Value.HasValue
            ? BsonUtils.ToMillisecondsSinceEpoch(new DateTime(dateRecord.Value.Value.Ticks, DateTimeKind.Utc))
            : 0;

        bsonWriter.WriteStartDocument();
        var keyValue = dateRecord.Key;
        bsonWriter.WriteString("key", string.IsNullOrEmpty(keyValue) ? string.Empty : keyValue);

        if (millisecondsSinceEpoch != 0)
        {
            bsonWriter.WriteDateTime("value", millisecondsSinceEpoch);
        }
        else
        {
            bsonWriter.WriteString("value", string.Empty);
        }

        bsonWriter.WriteEndDocument();
    }

    private static void SerializeDataRecordInt(BsonWriter bsonWriter, object value)
    {
        var intRecord = (DataRecordInt) value;
        bsonWriter.WriteStartDocument();

        var keyValue = intRecord.Key;
        bsonWriter.WriteString("key", string.IsNullOrEmpty(keyValue) ? string.Empty : keyValue);

        bsonWriter.WriteInt32("value", intRecord.Value);

        bsonWriter.WriteEndDocument();
    }
}
Craig Wilson

Also asked here: https://groups.google.com/forum/#!topic/mongodb-user/iOeEXbUYbo4

I think your better bet in this situation is to use a custom discriminator convention. You can see an example of this here: https://github.com/mongodb/mongo-csharp-driver/blob/v1.x/MongoDB.DriverUnitTests/Samples/MagicDiscriminatorTests.cs. While this example is based on whether a field exists in the document, you could easily base it on what type the field is (BsonType.Int32, BsonType.Date, etc...).

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

MongoDb custom collection serializer

From Dev

Cell not being added to collection view

From Dev

Creating a collection with custom Json serializer

From Dev

Custom Serializer with MongoDB, Jackson, and MongoJack

From Dev

GSON custom serializer for an object with a Collection field

From Dev

NodeJS/MongoDB - field is being added only once?

From Dev

Assembly reference not being added with custom nuget package

From Dev

Custom view not being added to FrameLayout for RecyclerView

From Dev

Adding serialization information for MongoDB custom serializer

From Dev

Jackson collection serializer error despite @JsonIgnore

From Dev

How to avoid widgets being added while moving back and on then clicking Next?

From Dev

Error on updating collection in MongoDB

From Dev

Custom Collection Cell Error

From Dev

Adding a replica in MongoDB throws an error

From Dev

Custom Collection (Of T)

From Dev

Constraints aren't being added through updateViewConstraints

From Dev

Constraints aren't being added through updateViewConstraints

From Dev

mongodb custom collection / document methods

From Dev

custom sort for a mongodb collection in meteor

From Dev

Custom error messages in Django Rest Framework serializer

From Dev

iqueryable where being ignored by Linq on custom collection

From Dev

Jackson 2.6.3 Deserialization avoid literal null added to collection

From Dev

In elastic search is a collection queryable while its being added to the index

From Dev

custom error page not being used

From Dev

custom error page not being used

From Dev

Custom error template throws fatal error

From Dev

Custom MKAnnotation in Swift not being added to map's annotations

From Dev

Custom NSCursor is being reset when subviews are added to a view in the window

From Dev

Preventing duplicates from being added to a Custom Adapter and database

Related Related

  1. 1

    MongoDb custom collection serializer

  2. 2

    Cell not being added to collection view

  3. 3

    Creating a collection with custom Json serializer

  4. 4

    Custom Serializer with MongoDB, Jackson, and MongoJack

  5. 5

    GSON custom serializer for an object with a Collection field

  6. 6

    NodeJS/MongoDB - field is being added only once?

  7. 7

    Assembly reference not being added with custom nuget package

  8. 8

    Custom view not being added to FrameLayout for RecyclerView

  9. 9

    Adding serialization information for MongoDB custom serializer

  10. 10

    Jackson collection serializer error despite @JsonIgnore

  11. 11

    How to avoid widgets being added while moving back and on then clicking Next?

  12. 12

    Error on updating collection in MongoDB

  13. 13

    Custom Collection Cell Error

  14. 14

    Adding a replica in MongoDB throws an error

  15. 15

    Custom Collection (Of T)

  16. 16

    Constraints aren't being added through updateViewConstraints

  17. 17

    Constraints aren't being added through updateViewConstraints

  18. 18

    mongodb custom collection / document methods

  19. 19

    custom sort for a mongodb collection in meteor

  20. 20

    Custom error messages in Django Rest Framework serializer

  21. 21

    iqueryable where being ignored by Linq on custom collection

  22. 22

    Jackson 2.6.3 Deserialization avoid literal null added to collection

  23. 23

    In elastic search is a collection queryable while its being added to the index

  24. 24

    custom error page not being used

  25. 25

    custom error page not being used

  26. 26

    Custom error template throws fatal error

  27. 27

    Custom MKAnnotation in Swift not being added to map's annotations

  28. 28

    Custom NSCursor is being reset when subviews are added to a view in the window

  29. 29

    Preventing duplicates from being added to a Custom Adapter and database

HotTag

Archive