Java can't deserialize JSON - java

I'm getting an error trying to deserialize what I believe is a valid JSON string:
String json = "{\"email\":\"testing#example.com\",\"password\":\"12345\"}";
// FlexJSON deserializer
JSONDeserializer<Signin> deserializer = new JSONDeserializer<Signin>();
// Deserialize into a Signin POJO.
Signin signin = deserializer.deserialize(json);
When I run this code, I get:
java.util.HashMap cannot be cast to com.myapp.server.Signin
java.lang.ClassCastException: java.util.HashMap cannot be cast to com.myapp.server.Signin
at com.myapp.server.SigninService.doPost(SigninService.java:39)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
... rest of stack trace omitted for brevity
Is my JSON malformed? It's almost as if the JSON is somehow "bad" and FlexJSON is treating it like a HashMap...

Looking at the documentation, the problem is that your json doesn't declare its class. This means that you explicitly need to supply a Class object to the deserializer, as in Java generics are only compile time, not runtime.
To quote from the documentation:
We need to replace the type information we just dropped when we instantiate the deserializer. To do that we'll pass the class we want to use into to flexjson.JSONDeserializer.deserialize(String, Class) method like so:
Hero hero = new JSONDeserializer<Hero>().deserialize( jsonHarvey, Hero.class );
So use:
Signin signin = deserializer.deserialize(json, Signin.class);

This library you're using seems like it's not supported really.
Seems it's not changed since 2010. I would try using something else.
I have used this one for example without any issues.
http://code.google.com/p/google-gson/downloads/list

Related

Getting "declares multiple JSON fields named" error when serializing with GSON

Type type = new TypeToken<HashMap<String , MovieDb>>() {}.getType();
gson.fromJson(json, type); //ERROR HERE !!!
When I convert from Json to the hashmap object it works fine without minifying enabled. But when minify is enabled it gives me the following error at this line:
java.lang.IllegalArgumentException: class a.a.a.a.b declares multiple JSON fields named a
at com.google.b.b.a.i.a(ReflectiveTypeAdapterFactory.java:172)
at com.google.b.b.a.i.a(ReflectiveTypeAdapterFactory.java:102)
at com.google.b.e.a(Gson.java:458)
at com.google.b.b.a.b.a(CollectionTypeAdapterFactory.java:53)
at com.google.b.e.a(Gson.java:458)
at com.google.b.b.a.i.a(ReflectiveTypeAdapterFactory.java:117)
at com.google.b.b.a.i.a(ReflectiveTypeAdapterFactory.java:166)
at com.google.b.b.a.i.a(ReflectiveTypeAdapterFactory.java:102)
at com.google.b.e.a(Gson.java:458)
at com.google.b.b.a.g.a(MapTypeAdapterFactory.java:126)
at com.google.b.e.a(Gson.java:458)
at com.google.b.e.a(Gson.java:926)
at com.google.b.e.a(Gson.java:892)
at com.google.b.e.a(Gson.java:841)
at com.techy.nateshmbhat.moviego.i.onPreExecute(MovieInterface.java:180)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:648)
at android.os.AsyncTask.execute(AsyncTask.java:595)
at com.techy.nateshmbhat.moviego.i.b(MovieInterface.java:101)
at com.techy.nateshmbhat.moviego.a.a(Activity_InTheaterMovies.java:55)
Your error is most propably because you minify two fields to a same name a. Something like:
#SerializedName("a")
Long veryLongFieldName;
#SerializedName("a")
Long anotherVeryLongFieldName;
Note that these can be either in the same class or if using inheritance it is enough that they are in the same inheritance tree. Gson can not assign two values to one minimized field name a.
It could of course also be that you have minimized some field to a name that is already reserved by some other unminimized field.
I've faced this issue when I had context property/variable in the model class

ArangoDB Java Driver with Kotlin data classes

Well, Arongo DB Java driver has no problems to store Kotlin data classes but it cannot load them back.
Showcase:
import com.arangodb.ArangoCollection
import com.arangodb.ArangoDB
import com.arangodb.entity.DocumentCreateEntity
fun main(args: Array<String>) {
// Get or recreate collection: "some_collection" in DB "test_db"
val collection: ArangoCollection = with(ArangoDB.Builder().build()!!.db("test_db")) {
if (!exists()) create()
with(collection("some_colelction")) {
if (!exists()) create()
this
}
}
// POJO as Kotlin data class
data class Foo(
val topic: String,
val answer: Int
)
val result: DocumentCreateEntity<Foo> = collection.insertDocument(Foo("The ultimate answer", 42))
// reusult have a key of the new document
// And in ArangoDB Web Interface you can see this document: {"answer":42,"topic":"The ultimate answer"}
// http://localhost:8529/_db/test_db/_admin/aardvark/index.html#collection/some_colelction/documents/
// But it doesn't work backwards
val foo: Foo = collection.getDocument(result.key, Foo::class.java)
}
Stacktrace:
Exception in thread "main" com.arangodb.ArangoDBException: com.arangodb.velocypack.exception.VPackParserException: java.lang.InstantiationException: MainKt$main$Foo
at com.arangodb.internal.util.ArangoDeserializerImpl.deserialize(ArangoDeserializerImpl.java:59)
at com.arangodb.internal.util.ArangoUtilImpl.deserialize(ArangoUtilImpl.java:58)
at com.arangodb.internal.ArangoExecutor.createResult(ArangoExecutor.java:112)
at com.arangodb.internal.ArangoExecutorSync$1.deserialize(ArangoExecutorSync.java:56)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:72)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:53)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:49)
at com.arangodb.internal.ArangoCollectionImpl.getDocument(ArangoCollectionImpl.java:134)
at com.arangodb.internal.ArangoCollectionImpl.getDocument(ArangoCollectionImpl.java:126)
at MainKt.main(main.kt:30)
Caused by: com.arangodb.velocypack.exception.VPackParserException: java.lang.InstantiationException: MainKt$main$Foo
at com.arangodb.velocypack.VPack.deserialize(VPack.java:398)
at com.arangodb.internal.util.ArangoDeserializerImpl.deserialize(ArangoDeserializerImpl.java:55)
... 9 more
Caused by: java.lang.InstantiationException: MainKt$main$Foo
at java.lang.Class.newInstance(Class.java:427)
at com.arangodb.velocypack.VPack.createInstance(VPack.java:488)
at com.arangodb.velocypack.VPack.deserializeObject(VPack.java:450)
at com.arangodb.velocypack.VPack.getValue(VPack.java:569)
at com.arangodb.velocypack.VPack.deserialize(VPack.java:396)
... 10 more
Caused by: java.lang.NoSuchMethodException: MainKt$main$Foo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 14 more
Kotlin’s data classes nicely serialized into expected JSON documents but seems like ArangoDB Java driver cannot load them back.
If I get the document as BaseDocument I have no problems to map it back to my data class using some JSON library but come on! There must be a better way or I definitely missed something.
The ArangoDB Java driver supports alternative serializer to de-/serialize documents, edges and query results. One implementation is VelocyJack which is based on Jackson working with jackson-dataformat-velocypack.
You should be able to configure it, that it is working together with the jackson-kotlin-module.
VelocyJack velocyJack = new VelocyJack();
velocyJack.configure((mapper) -> {
mapper.registerModule(new KotlinModule())
});
ArangoDB arango = new ArangoDB.Builder().serializer(velocyJack).build();
ArangoDB uses its own serialization framework - VelocyPack - to serialize and deserialize classes. As you can see in the code (and in the stacktrace you provided) that framework needs parameterless constructor to create an instance of deserialized class, which Kotlin's data classes do not have. As far as I know VelocyPack does not have a module for working with Kotlin data classes (like Jackson does), so your only option would be to create custom deserializer for your class and register it - it's possible with VelocyPack (see documentation), so I assume it's also possible in ArrangoDB.
Edit: Class ArrangoDB.Builder has method registerDeserializer(final Class<T> clazz, final VPackDeserializer<T> deserializer), which I assume can be used to register custom JSON deserializers for VelocyPack.
As a workaround for the vert.x project with com.fasterxml.jackson.module:jackson-module-kotlin dependency you can add a custom inlined extension function with a reified type so it will generically extract hashmap of the document then let Jackson’s Kotlin module to do the magic:
inline fun <reified T> ArangoCollection.getDoc(key: String): T =
JsonObject(getDocument(key, BaseDocument::class.java).properties).mapTo(T::class.java)!!
Then this line works with type inferring by the Kotlin compiler:
val for: Foo = collection.getDoc("document-key")
Known issues:
Does not consider ArangoDB document native properties like: _id, _key, _rev, _from, _to
Seems like Jackson still have issues with anonymous classes
Any ideas on how to improve it or how to reduce conversion overhead?
You have to make the fields in your data class mutable by using var instead of val. Val means the fields are final.
Next you need a parameterless constructor. You can achieve this by setting default values for the fields in your constructor or setting them null by default. If you decide to set them null you have to remove the Null safety from your fields by adding a '?' behind the data types.
More on removing Null Safety: https://kotlinlang.org/docs/reference/null-safety.html
Final data class with defaults:
data class Foo(
var topic: String = "",
var answer: Int = ""
)
Final data class with null:
data class Foo(
var topic: String? = null,
var answer: Int? = null
)
In your code you should use:
arango.setSerializer(VelocyJack...
instead of:
arango.serializer(VelocyJack...
otherwise you only use it for serializing and not for deserializing.
I created a pull request which you can use as workaround, using the kotlin no-arg maven plugin, here:
https://github.com/slavaatsig/arangodb-jackson-dataformat-velocypack-issue/pull/1/files
Even if the jackson KotlinModule works with data classes (as I verified here: https://github.com/rashtao/arangodb-jackson-dataformat-velocypack-issue/blob/dbg_serde/src/main/kotlin/com/example/issue/main.kt#L16-L21 ), somehow the driver tries accessing the no-arg constructor...
For further updates on this issue: https://github.com/arangodb/arangodb-java-driver/issues/202
fixed since java-velocypack 2.2.0: https://github.com/arangodb/java-velocypack#kotlin-data-classes

Error while Parsing json into scala case class

In my spring(mvc) web application, I am using org.codehaus.jackson.map.ObjectMapper in my scala code to map my json to scala objects using case classes. My Json String is an array of json objects objects. so I am using:
val user = mapper.readValue(myJson, classOf[List[MyClass]])
This line throws an error:
Exception in thread "main"
org.codehaus.jackson.map.JsonMappingException: Can not construct
instance of scala.collection.immutable.List, problem: abstract types
can only be instantiated with additional type inform
Am I using it right or is there any other way?
The problem is the Java type erasure. classOf[List[MyClass]] at runtime is the same as classOf[List[_]]. That is why Jackson cannot know, which types of the elements to create.
Luckily Jackson does support parsing with the JavaType, which describes the types themselves.
Here a simple sample in Java:
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class);
mapper.readValue(myJson, type);
Because of type erasure, the parameterized type of the List is lost at runtime.
Instead, use the Scala module for Jackson and you can simply do:
mapper.readValue(myJson, new TypeReference[List[MyClass]])
So long as the Scala module has been registered - this means a Scala List will be created.

Instantiate generic based on property file setting

I'm building a data driven test system. I have done this before in XML but json is giving me some interesting issues.
For each request and response type json, I have a setting in my script where I specify a pojo type. This type is instantiated to a class object thats passed to jackson to marshal the json into a usable pojo. so its like this:
"responseType": "java.util.List",
eventually gets pumped to
Class<?> reponseType = null;
try {
if (d.shouldPass) {
reponseType = Class.forName(d.responseType);
}
} catch (ClassNotFoundException e) {
throw new RequestResponseTypeInvalid(testName);
}
and I have usable class info to use in jackson. My problem is I need to do this:
"responseType": "java.util.List<foo>",
otherwise complex json types parse as hashmaps instead of pojo's. I suppose I can get creative and put something in to go from hashmap to pojo if I need to but I was wondering if there was any straight forward way to do this.
I suppose another way is to implement a factory class where I could say list_foo in the property file and have the factory class map that to an actual class object. That wouldn't be very hard but not as easy as just using the property.
thanks
You can't do this in the way that you're hoping, I'm afraid. Generics are a compile-time thing only, and can't be used in this way at runtime, because of type erasure.
The best you could do would be to have some list_foo properties, and map these explicitly to List<Foo> and so in in your code. But you can't do it by reflection.

How can I send the Object class to server with Jackson?

I have a Message class like this:
class Message {
#JsonProperty("content")
Object content;
}
where the content attribute can be a User, a Post, or a String
and I have to send this object to the server and cast the content to the right class.
I'm using Jackson annotations to serialize the JSON, but when I try to cast the content, an error appears, because the attribute content arrives in the server like a LinkedHashMap object.
The error is:
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/MegaRadarSocial].[Resteasy]] (http-localhost-127.0.0.1-8080-1) Servlet.service() for servlet Resteasy threw exception: org.jboss.resteasy.spi.UnhandledException: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to br.com.megaradar.megaradarsocial.model.User
I would like a help to casting...
Thanks
As you control both ends (server and client), you could try Genson library http://code.google.com/p/genson/. One of its features allows you to serialize to json and type information, this enables you to deserialize back to the right type.
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
json = genson.serialize(yourMessage);
// then deserialize it back
Message message = genson.deserialize(json, Message .class);
The serialized json will look like : {"content": {"#class":"package.path.Message", ...the object values...}}
You can even define aliases for the serialized classes
new Genson.Builder().addAlias("message", Message.class)
Important: Note that you need to use the same configuration of genson on both sides. So enable type information with setWithClassMetadata and if you use aliases, you must define the same on the client side.
What you need is #JsonTypeInfo annotation, like so:
class Message {
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY property="type")
#JsonProperty("content")
Object content;
}
(you can see http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html for examples)
which would add property "type" with class name as value (there are many alternative ways as well) when serializing, and using that when deserializing.
Thank you for all the answers. But I found another way to convert my Object to any type I want.
I'm using the method convertValue from the ObjectMapper object. Then, I can simulate the casting.
Thanks again

Categories