Deserializing BSON ReadBsonType can only be called when State is Type
Asked Answered
A

3

7

I have the following Code:

using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
using MongoDBTest;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace protocol.server.API.Clients
{
    public class ClientService : ServiceStack.Service
    {
        class CylinderSerializer : SerializerBase<Cylinder>
        {
            public override void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext context, MongoDB.Bson.Serialization.BsonSerializationArgs args, Cylinder value)
            {
                var wr = context.Writer;

                wr.WriteStartDocument();
                wr.WriteName("_id");
                wr.WriteObjectId(ObjectId.GenerateNewId());


              wr.WriteName("description");
                wr.WriteString(value.description.type);

                context.Writer.WriteEndDocument();
            }


        public override Cylinder Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args)
        {
            context.Reader.ReadStartDocument();
            Cylinder a = new Cylinder();
            a.Id = context.Reader.ReadObjectId();




            while (context.Reader.State != BsonReaderState.Type && context.Reader.ReadBsonType() != BsonType.EndOfDocument)
            { 
                a.description.type = context.Reader.ReadString();
                a.description.kind = context.Reader.ReadString();
                a.description.year = (short)context.Reader.ReadInt32();
                a.description.producer = context.Reader.ReadString();
            }
            return a;
        }



        public async Task<List<Cylinder>> Get(GetObjects request)
        {
            MongoDB.Bson.Serialization.BsonSerializer.RegisterSerializer(typeof(Cylinder), new CylinderSerializer());
            IMongoCollection<Cylinder> collection = Connect._database.GetCollection<Cylinder>("Cylinders");
            var results = await collection.Find(_ => true).ToListAsync();

            return results;
        }
    }
}

and get the error:

ReadBsonType can only be called when State is Type, not when State is Value

in line:

 while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)

I want to deserialize my objects, they look like this:

{ 
    "_id" : ObjectId("5826010eb831ee1c70df5f16"), 
    "description" : {
        "type" : "Cylinder", 
        "kind" : "rgdgg", 
        "year" : NumberInt(1997), 
        "producer" : "hnnghng", 
        "brands" : [
            "trhr"
        ], 
        "model" : [
            "Baws"
        ], 
        "internalproducerdesignation" : "tw6", 
        "origin" : "Greece"
    }, 
    "elements" : {
        "nonspringelements" : NumberInt(0), 
        "springelements" : NumberInt(11), 
        "discelements" : NumberInt(0), 
        "magneticelements" : NumberInt(0), 
        "activeelements" : NumberInt(11), 
        "passiveelements" : NumberInt(0), 
        "totalelements" : NumberInt(11)
    }, 
    "profiles" : [
        "d1", 
        "d11"
    ], 
    "certifications" : [
        "", 
        ""
    ], 
    "colors" : [
        "brown", 
        "chrome"
    ], 
    "specialfittings" : [
        "gf", 
        "hrthr", 
        "hgnn", 
        "ngnn", 
        "hngngn", 
        "nghnnn"
    ], 
    "cutdepths" : NumberInt(7), 
    "rareness" : "rare", 
    "value" : {
        "new" : "0", 
        "used" : "$50"
    }, 
    "Blaw" : {
        "tgtgt" : 10.0, 
        "hzhz" : true
    }, 
    "availableat" : "gtgtgtgt", 
    "specialabout" : "jujujuju", 
    "development" : {
        "predecessor" : "", 
        "follower" : "rfrfr"
    }, 
    "media" : [

    ]
}

My Clinder.cs :

using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization.Attributes;
using System;
using System.Collections.Generic;
using System.Globalization;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace protocol.server.API.Clients
{

    public class Cylinder
    {
        [BsonSerializer(typeof(ProductAttributeSerializer))]
        public class ProductAttributeSerializer : IBsonSerializer, IBsonArraySerializer
        {
            public Type ValueType { get { return typeof(List<string>); } }

            public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
            {
                var type = context.Reader.GetCurrentBsonType();
                List<String> items = new List<String>();

                switch (type)
                {
                    case BsonType.Document:


                    case BsonType.Array:

                        context.Reader.ReadStartArray();

                        while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
                        {
                            items.Add(context.Reader.ReadString());
                        }
                        context.Reader.ReadEndArray();
                        return new mode(items);

                    default:
                        throw new NotImplementedException($"No implementation to deserialize {type}");
                }
            }

            public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
            {

                var d = value;
                var attributes = value as List<string>;

                if (attributes != null)
                {
                    context.Writer.WriteStartArray();

                    foreach (string attr in attributes)
                    {
                        context.Writer.WriteString(attr);
                    }

                    context.Writer.WriteEndArray();
                }
            }

            public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
            {
                string elementName = null;
                var serializer = BsonSerializer.LookupSerializer(typeof(string));
                var nominalType = typeof(string);
                serializationInfo = new BsonSerializationInfo(elementName, serializer, nominalType);
                return true;
            }
        }


        [BsonId]
        public ObjectId Id { get; set; }

        [BsonSerializer(typeof(ProductAttributeSerializer))]
        public class mode
        {
            public mode(List<String> pItems)
            {
                this.items = new List<String>();
                this.items.Clear();
                this.items.AddRange(pItems);
            }
            public List<String> items { get; set; }
        }


        public class des
        {
            public string type { get; set; }
            public string kind { get; set; }
            public short year { get; set; }
            public string producer { get; set; }

            public List<string> brands { get; set; }

            public string internalproducerdesignation { get; set; }
            public string origin { get; set; }

            public mode model { get; set; }
        }
        public class elem
        {
            public short nonspringelements { get; set; }
            public short springelements { get; set; }
            public short discelements { get; set; }
            public short magneticelements { get; set; }
            public short activeelements { get; set; }
            public short passiveelements { get; set; }
            public short totalelements { get; set; }
        }
        public des description = new des();
        public elem elements = new elem();

        public IEnumerable<string> profiles { get; set; }
        public IEnumerable<string> certifications { get; set; }
        public IEnumerable<string> colors { get; set; }
        public IEnumerable<string> specialfittings { get; set; }
        public short cutdepths { get; set; }
        public string rareness { get; set; }

        public class val
        {
            public String @new { get; set; }
            public String used { get; set; }
        }
        public val value = new val();

        public class Pi
        {
            public Double difficulty { get; set; }
            public bool alreadypicked { get; set; }
        }
        public Pi Picking = new Pi();
        public string availableat { get; set; }
        public string specialabout { get; set; }

        public class devel
        {
            public string predecessor { get; set; }
            public string follower { get; set; }
        }

        public devel development = new devel();

        public Object[] media;


    }
}

How to prevent this error ? I just want to deserialize my objects...

Aldrin answered 19/11, 2016 at 11:28 Comment(0)
F
3
while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)

should be

while (context.Reader.State != BsonReaderState.Type || context.Reader.ReadBsonType() != BsonType.EndOfDocument)

Would cause to check the type if the state is a type. If it is not a type, you will pass and not check the type

Fort answered 23/11, 2016 at 11:52 Comment(0)
F
0

Not sure why to use while loop there if you just want to fill properties of one object (description). You can do it like this:

public override Cylinder Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) {
    context.Reader.ReadStartDocument();
    Cylinder a = new Cylinder();
    a.Id = context.Reader.ReadObjectId();
    context.Reader.ReadStartDocument();
    a.description.type = context.Reader.ReadString();
    a.description.kind = context.Reader.ReadString();
    a.description.year = (short) context.Reader.ReadInt32();
    a.description.producer = context.Reader.ReadString();
    return a;
}

Test (File bson.txt is copied verbatim from your question):

static void Main(string[] args) {
    var cylinder = new CylinderSerializer().Deserialize(BsonDeserializationContext.CreateRoot(new BsonDocumentReader(BsonDocument.Parse(File.ReadAllText(@"G:\tmp\bson.txt")))));
    Console.ReadKey();
}
Fradin answered 24/11, 2016 at 7:58 Comment(3)
Hey and thanks. Your first line (ReadStartDocument) runs into an error: ReadStartDocument can only be called when CurrentBsonType is Document, not when CurrentBsonType is Array. I cannot understand it, because I thought that the first thing to do is to call ReadStartDocument... and after that .ReadObjectId() and then .ReadName(). Another thing I cannot understand is why you use ReadStartDocument twice and why you don't use .ReadEndDocument()..Aldrin
I updated answer to show how I tested it using data you provided. As for ReadStartDocument - I call it second time because right after "_id" you have another document (complex object) - "description".Fradin
Hey. Thanks, but this does not help me.. As you can see I am using ServiceStack and I don't know why, but the problem I am confronted with seems to different from your testing with a local file. I have tested your local file deserialization and it works for me, but the documents I am using are retrieved by a MongoDB. I am going to add some Code. Error: ReadStartDocument can only be called when CurrentBsonType is Document, not when CurrentBsonType is Array.Aldrin
D
0

It's a lot of job to write own serializer this way. This is how i did to for cylinder. I managed to deserialize your sample this way.

Please mention, that there is a one simple help method there to deserialize string array. You don't have any class for "blaw" data, so i read it in not used variables.

    public override Cylinder Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        context.Reader.ReadStartDocument();
        Cylinder a = new Cylinder {Id = context.Reader.ReadObjectId()};

        context.Reader.ReadStartDocument();

        a.description.type = context.Reader.ReadString();
        a.description.kind = context.Reader.ReadString();
        a.description.year = (short)context.Reader.ReadInt32();
        a.description.producer = context.Reader.ReadString();

        context.Reader.ReadStartArray();
        a.description.brands = new List<string>();
        while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
        {
            a.description.brands.Add(context.Reader.ReadString());
        }
        context.Reader.ReadEndArray();

        context.Reader.ReadStartArray();
        a.description.model = new Cylinder.mode(new List<string>());
        while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
        {
            a.description.model.items.Add(context.Reader.ReadString());
        }
        context.Reader.ReadEndArray();

        a.description.internalproducerdesignation = context.Reader.ReadString();
        a.description.origin = context.Reader.ReadString();

        context.Reader.ReadEndDocument();
        context.Reader.ReadStartDocument();

        a.elements = new Cylinder.elem
        {
            nonspringelements = (short) context.Reader.ReadInt32(),
            springelements = (short) context.Reader.ReadInt32(),
            discelements = (short) context.Reader.ReadInt32(),
            magneticelements = (short) context.Reader.ReadInt32(),
            activeelements = (short) context.Reader.ReadInt32(),
            passiveelements = (short) context.Reader.ReadInt32(),
            totalelements = (short) context.Reader.ReadInt32()
        };

        context.Reader.ReadEndDocument();

        a.profiles =  readStringArray(context);
        a.certifications = readStringArray(context);
        a.colors = readStringArray(context);
        a.specialfittings = readStringArray(context);

        a.cutdepths = (short) context.Reader.ReadInt32();

        a.rareness = context.Reader.ReadString();

        context.Reader.ReadStartDocument();
        a.value = new Cylinder.val
        {
            @new = context.Reader.ReadString(),
            used = context.Reader.ReadString()
        };
        context.Reader.ReadEndDocument();

        context.Reader.ReadStartDocument();
        var blawInt = context.Reader.ReadDouble();
        var blawBool = context.Reader.ReadBoolean();
        context.Reader.ReadEndDocument();

        a.availableat = context.Reader.ReadString();
        a.specialabout = context.Reader.ReadString();

        context.Reader.ReadStartDocument();
        a.development = new Cylinder.devel
        {
            predecessor = context.Reader.ReadString(),
            follower = context.Reader.ReadString()
        };
        context.Reader.ReadEndDocument();

        var objects=new List<object>();
        context.Reader.ReadStartArray();
        while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
        {
            objects.Add(context.Reader.ReadString());
        }
        context.Reader.ReadEndArray();
        a.media = objects.ToArray();

        context.Reader.ReadEndDocument();
        return a;
    }

    private static IEnumerable<string> readStringArray(BsonDeserializationContext context)
    {
        context.Reader.ReadStartArray();
        var strings = new List<string>();
        while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
        {
            strings.Add(context.Reader.ReadString());
        }
        context.Reader.ReadEndArray();
        return strings;
    }
Dumbarton answered 29/11, 2016 at 13:53 Comment(2)
Hey and thanks, I don't understand why this error occurs, but I get a ReadObjectId can only be called when CurrentBsonType is ObjectId, not when CurrentBsonType is Int32 using your code...Aldrin
sorry, code was not complete, now it works and could deserialize exact the document you posted, i don't know, what are another documents in your collection and still think, that it's not the best way to deserialize your data.Dumbarton

© 2022 - 2024 — McMap. All rights reserved.