bguerout/jongo

Jongo not serializing / deserializing Map correctly.

Closed this issue · 1 comments

I am using Jongo ("uk.co.panaxiom" % "play-jongo_2.11" % "2.0.0-jongo1.3") with Play framework. I have a User class, with a Map field. The key of the map is my own custom Java nested class MyObject, and the value is a Date. I want to store, update, and retrieve User objects in a Mongo collection.

I got the problem "The dotted field ... in ... is not valid for storage," because a key in the map had a dot (".") in it. To resolve this, I specified my own JsonSerializer (which replaced the "$" and "." in the map's keys) for the MyObject class. I did that and things ran fine.

But then, when I try to update a user by id in Mongo, I get the below error. I tried variations of the JsonSerializer for MyObject as well, as shown below. But still, I get similar error. From the error, it seems I cannot write the String right away in the Serializer, so I tried adding a field name (I'm not sure which to use, since this is just the key for a Java Map), but that didn't work either. I have also verified that the deserialization and serialization are in fact inverses (i.e. serialize(deserialize(object)) == object). How do I resolve this?

For example, I also looked at https://gist.github.com/crimsoncor/3335224, and that uses an ObjectMapper. I'm not using it. Do I have to? Does that have anything to do with it?

My Error / stack trace:
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[IllegalArgumentException: Cannot parse query: {$set:#}]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:280)
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:206)
at play.api.GlobalSettings$class.onError(GlobalSettings.scala:160)
at play.api.DefaultGlobal$.onError(GlobalSettings.scala:188)
at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:98)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:100)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:344)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:343)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248)
at scala.concurrent.impl.Promise$DefaultPromise.link(Promise.scala:304)
at scala.concurrent.impl.Promise$DefaultPromise.linkRootOf(Promise.scala:289)
at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:253)
at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:249)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:91)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:72)
at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:90)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:409)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.lang.IllegalArgumentException: Cannot parse query: {$set:#}
at org.jongo.query.BsonQueryFactory.createQuery(BsonQueryFactory.java:162)
at org.jongo.Update.with(Update.java:52)
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$64$$anonfun$apply$64.apply(Routes.scala:2297)
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$64$$anonfun$apply$64.apply(Routes.scala:2297)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:157)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:156)
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:136)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:73)
at play.http.HttpRequestHandler$1.call(HttpRequestHandler.java:54)
at controllers.interceptor.UserLookupInterceptor.call(UserLookupInterceptor.java:61)
at controllers.interceptor.UserLookupInterceptor.call(UserLookupInterceptor.java:61)
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:108)
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:108)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:56)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70)
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:48)
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:492)
at play.core.j.JavaAction.apply(JavaAction.scala:108)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5$$anonfun$apply$6.apply(Action.scala:112)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5$$anonfun$apply$6.apply(Action.scala:112)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5.apply(Action.scala:111)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5.apply(Action.scala:110)
at scala.Option.map(Option.scala:146)
at play.api.mvc.Action$$anonfun$apply$2.apply(Action.scala:110)
at play.api.mvc.Action$$anonfun$apply$2.apply(Action.scala:103)
at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:251)
... 14 common frames omitted
Caused by: org.jongo.marshall.MarshallingException: Unable to marshall parameter: [id=58f980a98041ac25386096ac]
at org.jongo.query.BsonQueryFactory.marshallParameter(BsonQueryFactory.java:209)
at org.jongo.query.BsonQueryFactory.access$000(BsonQueryFactory.java:35)
at org.jongo.query.BsonQueryFactory$1.objectDone(BsonQueryFactory.java:141)
at com.mongodb.util.JSONParser.parseObject(JSON.java:274)
at com.mongodb.util.JSONParser.parse(JSON.java:227)
at com.mongodb.util.JSONParser.parseObject(JSON.java:263)
at com.mongodb.util.JSONParser.parse(JSON.java:227)
at com.mongodb.util.JSONParser.parse(JSON.java:155)
at com.mongodb.util.JSON.parse(JSON.java:92)
at org.jongo.query.BsonQueryFactory.createQuery(BsonQueryFactory.java:123)
... 46 common frames omitted
Caused by: org.jongo.marshall.MarshallingException: Unable to marshall [id=58f980a98041ac25386096ac] into bson
at org.jongo.marshall.jackson.JacksonEngine.marshall(JacksonEngine.java:60)
at org.jongo.query.BsonQueryFactory.marshallDocument(BsonQueryFactory.java:234)
at org.jongo.query.BsonQueryFactory.marshallParameter(BsonQueryFactory.java:206)
... 55 common frames omitted
Caused by: com.fasterxml.jackson.core.JsonGenerationException: Can not write string, expecting field name
at com.fasterxml.jackson.core.JsonGenerator._reportError(JsonGenerator.java:1723)
at de.undercouch.bson4jackson.BsonGenerator._verifyValueWrite(BsonGenerator.java:397)
at de.undercouch.bson4jackson.BsonGenerator.writeString(BsonGenerator.java:417)
at topl.models.User$MyObjectKeySerializer.serialize(User.java:874)
at topl.models.User$MyObjectKeySerializer.serialize(User.java:864)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:593)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:519)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:31)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:672)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1428)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1129)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:967)
at org.jongo.marshall.jackson.JacksonEngine.marshall(JacksonEngine.java:58)
... 57 common frames omitted

My code:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.bson.types.ObjectId;

public class User {
  @JsonProperty("_id")
  private ObjectId id;

  @JsonDeserialize(keyUsing = MyObjectKeyDeserializer.class) @JsonSerialize(keyUsing = MyObjectKeySerializer.class)
  private Map<MyObject, Date> myObjectToDate = Maps.newHashMap();

  private static class MyObjectKeySerializer extends JsonSerializer<MyObject> {
    @Override
    public void serialize(MyObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        println("serializing: " + value);
        String serialized = serializeFromMyObject(value);
        println("serialized: " + serialized);
        gen.writeString(serialized);
    }
}

  private static class MyObjectKeyDeserializer extends KeyDeserializer {
    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
      println("deserializing: " + key);
      MyObject toReturn = convertStringToMyObject(key);
      println("deserialized: " + toReturn);
      return toReturn;
    }
  }
}
stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.