My json Object is dynamic, there may be arounf 10 -15 types of dynamic json response i will get,
EX: {"a": "B"}, {"a": [a, c, d]}, {a:b, d: []}, {a: []}, {a: [], b:[]}
these are possible types i have define.
//Before writing the below line, I have to identify the response belongs
to the correct Class Type and Convert the response into the corosponding Java Class.
A aResponse = mapper.convertValue(jsonResponse(), A.class );
Based on my above code the response always consider to take A.class and will throw an exception.
How can i identify the response belongs to specifc class, and convert it?
You can use a custom deserializer for this:
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
final SimpleModule module = new SimpleModule("configModule", Version.unknownVersion());
module.addDeserializer(Root.class, new DeSerializer());
mapper.registerModule(module);
Root readValue = mapper.readValue(<json source>);
}
}
class DeSerializer extends StdDeserializer<Root> {
protected DeSerializer() {
super(Root.class);
}
#Override
public Root deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
// use p.getText() and p.nextToken to navigate through the json, conditionally check the tags and parse them to different objects and then construct Root object
return new Root();
}
}
Related
Because it's a bad habit to leave raw types like List<String> on their own in the application I decided to encapsulate it using the following class:
public class EncapsulatedList {
#JsonProperty
private List<String> someWords;
/*
Some setters, getters and so on
*/
}
But it's serialized to:
{
"someWords": [
"cheese",
"random cheese",
"more random cheese"
]
}
It would be a lot nicer to have it as a plain list like:
[
"cheese",
"random cheese",
"more random cheese"
]
Is there a clean way to achieve this using Jackson 2 without having to do this explicitly like deserializing the list first and putting it into the encapsulating class?
From the JavaDoc of #JsonUnwrapped:
Also note that annotation only applies if
Value is serialized as JSON Object (can not unwrap JSON arrays using this mechanism)
If you don't want to use a String[] or List<String> directly then you can always deserialize the type yourself, like:
class EncapsulatedListDeserializer extends StdDeserializer<EncapsulatedList> {
// ctor omitted
public EncapsulatedList deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
List<String> list = new ArrayList<>();
if (node.isArray()) {
for (JsonNode value : node) {
list.add(value.asText());
}
}
return new EncapsulatedList(list);
}
}
At least that somewhat abstracts the deserialization and calling the setter manually.
I am using Jackson to parse object. sometime I need list of objects.
when I am using like this its working
List<MyObject> mapper.readValue(file , new TypeReference<MyObject>() {})
but when I am using it like this its not working
public class JsonMocksRepository<T>{
public T getObject() throws Exception{
return mapper.readValue(file ,new TypeReference<T>());
}
}
What I need to do ?
Basically I want to use generics to get the right class
This is because of type erasure. There is no information about the actual type represented by T available at runtime, so your TypeReference will be effectively be simply TypeReference<Object>.
If you want a generic instance of JsonMocksRepository, you will need to inject the TypeReference at construction time:
public class JsonMocksRepository<T>{
private final TypeReference<T> typeRef;
public JsonMocksRepository(TypeReference<T> typeRef) {
this.typeRef = typeRef;
}
public T getObject() throws Exception{
return mapper.readValue(file, typeRef);
}
}
I have this piece of code, that serialises a subclass of Map using Jackson.
Without the serializer registered I get a valid json, I use the serializer to get the name to lower case.
However, the produced json looks like:
{:"two":"aaa":"one":"aaa"}
Any idea why? How to fix?
#Test
public void test_serialization() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("JSONModule", new Version(1, 0, 0, null, null, null));
module.addSerializer(Circle.class, new CircleSerializer());
module.addKeySerializer(Circle.class, new CircleSerializer());
mapper.registerModule(module);
CircleMap statistics = new CircleMap();
statistics.put(Circle.ONE, "aaa");
statistics.put(Circle.TWO, "aaa");
System.out.println(mapper.writeValueAsString(statistics));
}
enum Circle {
ONE, TWO
}
static class CircleMap extends HashMap<Circle, String> {
}
static class CircleSerializer extends JsonSerializer<Circle> {
#Override
public void serialize(Circle value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
gen.writeString(value.name().toLowerCase());
}
}
Replace
gen.writeString(value.name().toLowerCase());
with
gen.writeFieldName(value.name().toLowerCase());
You're serializing a key. That's interpreted as a JSON field name so you have to use that method. writeString javadoc states
Method for outputting a String value. Depending on context this means
either array element, (object) field value or a stand alone String;
You're straight up writing the string value. That's not what you want.
Note that you'll need a different serializer for keys and for normal values.
I need a mechanism for calling remote methods on server which handles type hierarchy correctly. For example GWT can do this, but my client isn't javascript! By handling type hierarchy, I mean transfering child objects even when the type is declared as a parent class! Suppose we have a class Container:
class Parent {
String name;
}
class Child extends Parent {
String family;
}
class Container {
Parent p;
}
And I have a method on server with the following signature:
void doSomethingImportant(Container c) {}
If I call this method on client, and pass an instance of Container, which has an instance of Child as property "p", I expect to get an instance of Child on server too (which would have "family" property)!
GWT handles this without any problem, is there any other technologies that can handle this?
I didn't find a RPC mechanism for this, but I managed to use JSON in order to handle this. I found Gson which is google's API for using JSON in java. It converts objects to JsonElements which can be interpretted as Strings and vice versa.
So the key feature that helped me develop this, was Gson's custom Serializer/Deserializer. I implemented a class which is Serializer and Deserializer for Object, and I send the class name for source class along the class's content:
class MySerializerAndDeserializer implements JsonSerializer<Object>, JsonDeserializer<Object>{
public JsonElement serialize(Object src, Type typeOfSrc,
JsonSerializationContext context) {
Class clazz = src.getClass();
JsonElement serialize = context.serialize(src);
JsonArray array = new JsonArray();
array.add(new JsonPrimitive(clazz.getName()));
array.add(serialize);
return array;
}
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
String asString = array.get(0).getAsString();
Object deserialize = null;
try {
deserialize = context.deserialize(array.get(1).getAsJsonObject(), Class.forName(asString));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return deserialize;
}
}
and then I registered MySerializerAndDeserializer for Parent.class:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Parent.class, new MySerializerAndDeserializer());
And finally used gson and got the Instance I expected correctly:
String json = gson.toJson(container, Container.class);
Container containerFromJson = gson.fromJson(json, Container.class);
Using sfnrpc (http://code.google.com/p/sfnrpc) you can specify which type must be used optionally in the class arguments.
Hope this helps.
Object [] objArr = new Object[1];
TestObject to = new TestObject();
to.data = "HELLO,";
objArr[0] = to;
**Class [] classArr = new Class[1];
classArr[0] = TestObject.class;**
TestObject response = (TestObject)rpcc.invoke("#URN1","127.0.0.1:6878","echo",true,60,"", objArr,classArr);
sl.debug("The response was:"+response.data);
Is it possible to deserialize JSON using Jackson into one of two types based on the content of the JSON?
For example, I have the following Java (technically Groovy, but that's not important) interfaces and classes:
interface Id {
Thing toThing()
}
class NaturalId implements Id {
final String packageId
final String thingId
Thing toThing() {
new PackageIdentifiedThing(packageId, thingId)
}
}
class AlternateId implements Id {
final String key
Thing toThing() {
new AlternatelyIdentifiedThing(key)
}
}
The JSON I will receive will look like either of the following:
This JSON should map to NaturalId {"packageId": "SomePackage", "thingId": "SomeEntity"}
This JSON should map to AlternateId {"key": "SomeUniqueKey"}
Does anyone know how I can accomplish this with Jackson 2.x WITHOUT including type id's?
Are these the only two classes that implement Id? If so, you could write an IdDeserializer class and put #JsonDeserialize(using = IdDeserializer.class) on the Id interface, and the deserializer would look at the JSON and determine which object to deserialize into.
EDIT: The JsonParser is streaming so it should look something like this:
public Id deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectNode node = jp.readValueAsTree();
Class<? extends Id> concreteType = determineConcreteType(node); //Implement
return jp.getCodec().treeToValue(node, concreteType);
}
Annotate your methods with #JsonIgnore
#JsonIgnore
Thing toThing() {
new PackageIdentifiedThing(packageId, thingId)
}
With Jackson2, you can easily marshall to different classes using generics:
private <T> T json2Object(String jsonString, String type, Class<T> clazz) {
JsonNode jsonObjectNode = getChildNode(jsonString, type);
T typeObject = null;
try {
typeObject = jacksonMapper.treeToValue(jsonObjectNode, clazz);
} catch (JsonProcessingException jsonProcessingException) {
LOGGER.severe(jsonProcessingException);
}
return typeObject;
}