BulkWrite operation in MongoDB failing with invalid escape sequence

0

I am running the code below and receiving the subsequent error:

Code:

using MongoDB.Bson;
using MongoDB.Driver;
using System.Collections.Generic;

namespace MongoBulkWriteSerializationFailure
{
    class Person
    {
        public string Firstname;
        public string Lastname; 
    }

    class DummyClient
    {
        private IMongoCollection<BsonDocument> _collection;

        public DummyClient()
        {
            _collection = (new MongoClient("mongodb://root:password@localhost:27017/")).GetDatabase("my_db").GetCollection<BsonDocument>("persons");
        }

        public void Upsert(Person person)
        {
            List<WriteModel<BsonDocument>> list_of_operations = new List<WriteModel<BsonDocument>>();
            ReplaceOneModel<BsonDocument> write_model = new ReplaceOneModel<BsonDocument>($"{{ 'firstname': '{person.Firstname}', 'lastname': '{person.Lastname}'}}", person.ToBsonDocument());
            write_model.IsUpsert = true;
            list_of_operations.Add(write_model);
            BulkWriteResult<BsonDocument> outcome = _collection.BulkWrite(list_of_operations, new BulkWriteOptions { IsOrdered = false });
            System.Console.WriteLine($"Processed {outcome.ProcessedRequests.Count} item(s)");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            DummyClient client = new DummyClient();

            // Successful Control test:
            ((new MongoClient("mongodb://root:password@localhost:27017/")).GetDatabase("my_db").GetCollection<BsonDocument>("persons")).InsertOne((new Person { Firstname = "John\\1", Lastname = "Smith" }).ToBsonDocument());

            // Successful operation:
            client.Upsert(new Person { Firstname = "Mike", Lastname = "Smith" });

            // Failing operation:
            client.Upsert(new Person { Firstname = "Nick\\1", Lastname = "Smith" });
        }
    }
}

Error:

System.FormatException
  HResult=0x80131537
  Message=Invalid escape sequence in JSON string '\1'.
  Source=MongoDB.Bson
  StackTrace:
   at MongoDB.Bson.IO.JsonScanner.GetStringToken(JsonBuffer buffer, Char quoteCharacter)
   at MongoDB.Bson.IO.JsonScanner.GetNextToken(JsonBuffer buffer)
   at MongoDB.Bson.IO.JsonReader.PopToken()
   at MongoDB.Bson.IO.JsonReader.ReadBsonType()
   at MongoDB.Bson.Serialization.Serializers.BsonDocumentSerializer.DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.Serializers.BsonValueSerializerBase`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   at MongoDB.Bson.BsonDocument.Parse(String json)
   at MongoDB.Driver.JsonFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.MongoCollectionImpl`1.ConvertWriteModelToWriteRequest(WriteModel`1 model, Int32 index)
   at System.Linq.Enumerable.<SelectIterator>d__5`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at MongoDB.Driver.Core.Operations.BulkMixedWriteOperation..ctor(CollectionNamespace collectionNamespace, IEnumerable`1 requests, MessageEncoderSettings messageEncoderSettings)
   at MongoDB.Driver.MongoCollectionImpl`1.CreateBulkWriteOperation(IEnumerable`1 requests, BulkWriteOptions options)
   at MongoDB.Driver.MongoCollectionImpl`1.BulkWrite(IClientSessionHandle session, IEnumerable`1 requests, BulkWriteOptions options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass23_0.<BulkWrite>b__0(IClientSessionHandle session)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSession[TResult](Func`2 func, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.BulkWrite(IEnumerable`1 requests, BulkWriteOptions options, CancellationToken cancellationToken)
   at MongoBulkWriteSerializationFailure.DummyClient.Upsert(Person person) in C:\code\MongoBulkWriteSerializationFailure\Program.cs:line 28
   at MongoBulkWriteSerializationFailure.Program.Main(String[] args) in C:\code\MongoBulkWriteSerializationFailure\Program.cs:line 46

As it would be noted there are some control operations in the main program which demonstrate that data can be inserted in the target DB as well as data that contains the offending sub-string in the BulkWrite() operation. I am testing against version 4.0.0 of MongoDB and using version 2.7.0 of the official Mongo C# driver.

Would anyone know what I could be missing in terms of preparing the data (e.g. escaping) prior to the BulkWrite() operation? To be precise, applying another pair of '\' before the '\' solves the problem (so, Nick\\1 becomes Nick\\\\1). But I have a feeling that this is a bit hacky as there could be other corner-cases that would break the serialization process. So, I am hoping that there is an 'escape function' or 'wrapper object' provided by the SDK that could be leveraged in order to address these serialization traps.

c#
mongodb
asked on Stack Overflow Jul 31, 2018 by Gros Lalo • edited Jul 31, 2018 by Gros Lalo

1 Answer

0

If you build the FilterDefinition yourself in the Upsert function like this:

  public void Upsert(Person person)
    {
        var fiterBuilder = Builders<BsonDocument>.Filter;

        var escapedFirstname = filter_builder.Eq("Firstname", person.Firstname);
        var escapedLastname = filter_builder.Eq("Lastname", person.Lastname);

        var filter = fiterBuilder.And(escapedFirstname, escapedLastname);

        List<WriteModel<BsonDocument>> list_of_operations = new List<WriteModel<BsonDocument>>();
        ReplaceOneModel<BsonDocument> write_model = new ReplaceOneModel<BsonDocument>(filter, person.ToBsonDocument());
        write_model.IsUpsert = true;
        list_of_operations.Add(write_model);
        BulkWriteResult<BsonDocument> outcome = _collection.BulkWrite(list_of_operations, new BulkWriteOptions { IsOrdered = false });
        System.Console.WriteLine($"Processed {outcome.ProcessedRequests.Count} item(s)");
    }

It will escape the characters correctly.

And if you wish to know how the Driver does this you can see this happen here: https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Bson/IO/JsonScanner.cs#L33

answered on Stack Overflow Jul 31, 2018 by Skami • edited Aug 1, 2018 by Skami

User contributions licensed under CC BY-SA 3.0