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;
}
}
}
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.