Dynamically entering class literal as method argument at runtime - java

I have an application which makes use of an external library (Jackson), and the method I need requires a class literal as an argument. So if I wish to parse my JSON string into a User object:
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("user.json"), User.class);
Now, I wish to use this method dynamically (i.e. parse different JSON strings using the same line of code). For example:
String json1 = "{"type":"jacket",...}";
String json2 = "{"type":"sweater",...}";
Object object = mapper.readValue(json1/json2, ???);
//returns a Jacket object OR Sweater object based on the "type" key
//i.e. use Jacket.class as the 2nd argument if "type" is "jacket"
//OR Sweater.class if "type" is "sweater"
//After getting the deserialized object,
//if object is Jacket, cast as a Jacket
//if object is Sweater, cast as a Sweater
Of course, the JSON string in question can be for any class, so I can't simply hard-code an if-else loop. I've looked at custom serializers, but frankly am quite lost at what it's talking about, and would like some help in how I can go about this.
In summary, I need some way to first define a class literal from a String, and then cast the resulting Object into the specific class (but my focus is on getting readValue to work dynamically).

Looks like you need a mapping somewhere between JSON type variable and Java class type.
Generally result should be something like this map:
Map<String, Class<? extends YourSupertype>> map = new HashMap<>();
map.put("sweater", Sweater.class);
map.put("jacket", Jacket.class);
Just store possible clothing types somewhere in a file, then do something like:
String clothingType = nextEntryFromFile();
String className = constructClassNameFromClothingType(clothingType);
map.put(clothingType, Class.forName(className));

Since version 1.5 Jackson supports Polymorphic Type Handling, check here http://www.cowtowncoder.com/blog/archives/2010/03/entry_372.html
there are examples on how to correctly handle deserialization in those cases.

Related

Java - Get all local field values of an object as map

I want to retrieve a map of field-values present in a java object. I tried com.google.Gson and org.apache.commons.lang3.builder.ToStringBuilder to retrieve all the details regarding the given object. But following problems occured :
com.google.Gson:
Gson gson = new GsonBuilder.create();
String str = gson.reflectionToString(myObject);
StackOverFlowError occurs for some objects, even a map having JSONObject fails miserably.
org.apache.commons.lang3.builder.ToStringBuilder:
String str = ToStringBuilder.reflectionToString(myObject);
While using the above API for HashMap, variables inside the HashMap are displayed but contents of the HashMap are not visible.
Output :
samplePackage.CustomHashMap#5fec459b[accessOrder=false,threshold=12,loadFactor=0.75]
Help me to find the perfect solution !
You could try using reflection. This allows you to use your class name to access your class and its meta data. You can then use the getDeclaredFields method to get all the declared Fields for your class. Your fields can be private this way so they can't be edited by outside classes without calling getters or setters.
//initialize the name of your class.
String className = "examplePackageName.exampleClassName";
//get your class using the name of the class
Class<? extends ObjectImplementation> Classtouse = Class.forName(className);
//initialize map of fieldnames to field objects from reflection
Map<String,Field> FieldMap = new HashMap<>();
//gets delcared fields as a List, converts it to an array and for each element in the
//array, it maps the element's name to the element itself as a field object
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field));
//now you have your fieldmap ready to use.
when you want to access the value of a Field, you can use
//instantiate an object of your Class with type Classtouse here
//use the object created in the line above and set its fields using the line
//below
field.get(//Object that holds this field);
Where field is the Field object you want to edit which you accessed using the fieldname or some other method. If you want to map fieldnames to field values directly you can edit this from the code above:
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field));
to be:
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field.get(//your object here)));
Just make sure you have instantiated an object you want to do this with first.
An example of this and other reflection methods can be found in the Field javadoc
Here:[link]https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html
This code is not garenteed to be perfectly runnable but it's close.

How to set an ArrayList Type dynamically using the class name?

Using the Gson library, I convert a JSON string to an ArrayList Object by following code:
posts = gson.fromJson(json, new TypeToken<List<Post>>(){}.getType());
and I use the following code for convert a json to a class objec:
obj = gson.fromJson(json, Class.forName(fullClassName));
You see in the second code that I used reflection to specify the type of class;
but for the first code, I don't know how to specify the type of list using reflection.
What do I do?!
So, from what I understand you need to deserialize list of objects but type isn't known on compile time but you are providing it dynamically?
You probably want to use something like that for your type:
TypeToken.getParameterized(List.class, Class.forName("foo.bar.MyDynamicType"))
This will create same type as
new TypeToken<List<MyDynamicType>>(){}.getType()

In jackson 2.8, how can I postprocess every deserialized value of class Foo?

I'm building a rest service using jackson with a single instance of ObjectMapper where I can set my configuration. Java-side values are pojos with fields of types like String and int. Very simple, straightforward situation, nothing special.
I want to perform some processing on every field of a given type after deserialization, possibly altering the value that should be put in the pojo field. I don't want to litter my pojos with annotations or anything, it should be self-contained within ObjectMapper. I also don't want to override the existing deserialization code - the data mapping itself should keep working as-is.
Concrete example: say I want to call toUpperCase() on every incoming String because I dislike lower case letters. How can I create this behavior? I was hoping to find something like the following, but it doesn't seem to exist:
objectMapper.getDeserializationConfig().registerValueProcessor(Foo.class, Foo::bar);
I'm familiar with jackson basics like registering a new type (de)serializer, I just don't know anything for this particular type of thing.
Concrete example: say I want to call toUpperCase() on every incoming String because I dislike lower case letters. How can I create this behavior?
Create your custom deserializer for String:
SimpleModule module = new SimpleModule("customDeserializers", Version.unknownVersion());
module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return jp.getValueAsString().toUpperCase();
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
The custom deserializer defined above will process only values mapped to String. If a given POJO has a member where its type is UUID, for instance, the value won't be converted to upper case. See the example below:
public class Person {
private UUID id;
private String name;
// Getters and setters
}
String json = "{\"id\": \"6d39b716-ee80-4468-b1e4-b4270d57be99\", \"name\": \"Joe Doe\"}";
Person person = mapper.readValue(json, Person.class);
System.out.println(person.getId()); // Prints 6d39b716-ee80-4468-b1e4-b4270d57be99
System.out.println(person.getName()); // Prints JOE DOE
I guess the best thing to do would be to write my own (de)serializer impl that wraps around an existing (de)serializer, using inheritance or composition, and replaces it in the objectmapper configuration. That way I can call the original conversion logic (through super or a field) and postprocess the result it returns.

Creating Map or Set using GSON

I am using Google's GSON library and want to create a JSON which looks something like this:
{
"teamSet":[],
"classificationMap" : {}
}
Notice that [] and {} are empty Set and Map respectively and are not surrounded with double quotes.
I want to create this Json using the GSON add/addproperty method and not by converting a Java class into JSON using the Gson.toJson() method. The business use-case restricts me from creating specific Java classes as the JSON structure may change overtime. So I cannot use a class like this as this would rigidly tie a JSON structure with a concrete class
class Linkage{
private Set<String> teamSet;
private Map<String, String> classificationMap;
// getter, setter methods follow
...
}
When I use the GSON.addproperty(genericObject, type), it is appending double quotes around [] and {}. I am using couchbase for my DB requirements and the double quotes around [] and {} makes couchbase treat them as string and not as Set/Map. This renders my Map-Reduce views useless and buggy :(
Please let me know if its possible to create such a JSON without having to tie it up with a concrete JAVA class. Thanks !
My current code looks like this:
// create types
Type setType = new TypeToken<Set<String>>() {}.getType();
Type mapType = new TypeToken<Map<String, String>>() {}.getType();
Gson GSON = new Gson();
Set<String> teams = new HashSet<String>();
Map<String, String> classificationMap = new HashMap<String, String>();
JsonObject linkageJson = new JsonObject();
linkageJson.addProperty("teamSet", GSON.toJson(teams, setType));
linkageJson.addProperty("classificationMap", GSON.toJson(classificationMap, mapType));
In the 2.x line of the couchbase java sdk, there is the JsonObject class that could have fit your need.
It is perfect to create Json "by hand" and still have a simple generic object representation, and is the official way of putting json into the database via the sdk.
It would go like this :
JsonObject obj = JsonObject.create();
obj.put("teamSet", JsonArray.from(new ArrayList(teams)))
.put("classificationMap", JsonObject.from(classificationMap));
Granted this is a little bit contrived because arrays and sub-objects can only be constructed from respectively List<?> and Map<String, ?> factory methods. Also the class support a limited set of value types (no custom classes, only String, Number, Boolean, etc...).
i feel somewhere it is storing it toString() representation.
Please refer below link, it might help you.
click here

JSON Polymorphism

I have a List of javascript objects on my client side, which are a list of "events" that a user has executed. When the user is ready, I want to send this to the server. The order of events is important, so preserving the list order is necessary.
What I would like to do is to have a JSON library (don't mind which one) to bind the JSON to some Event objects in my Java code, where Event is an abstract class, and I have 3 concrete classes that all extend Event (lets say EventA, EventB and EventC).
Ideal scenario would be something like
List<Event> events = jsonlibrary.deserialise(jsonString);
which may contain a list of items such as
[eventA, eventC, eventA, eventA, eventB]
Is this possible, or do I have to inspect the JSON tree manually, and deserialise the individual elements of the json array?
JSON objects are just key/value pairs and contain no type information. That means identifying the type of a JSON object automatically isn't possible. You have to implement some logic on the server-side to find out what kind of event you are dealing with.
I would suggest to use a factory method which takes a json string, parses it to find out what kind of Event it is, builds an Event object of the correct subclass and returns it.
You could use Genson library http://code.google.com/p/genson/.
It can deserialize to concrete types if the json was produced using Genson. Otherwise you only need to add something like [{"#class":"my.java.class", "the rest of the properties"}...]
// an example
abstract class Event {
String id;
}
class Click extends Event {
double x, y;
}
// you can define aliases instead of plain class name with package (its a bit nicer and more secure)
Genson genson = new Genson.Builder().setWithClassMetadata(true).addAlias("click",
Click.class).create();
String json = "[{\"#class\":\"click\", \"id\":\"here\", \"x\":1,\"y\":2}]";
// deserialize to an unknown type with a cast warning
List<Event> events = genson.deserialize(json, List.class);
// or better define to which generic type
GenericType<List<Event>> eventListType = new GenericType<List<Event>>() {};
events = genson.deserialize(json, eventListType);
EDIT
here is the wiki example http://code.google.com/p/genson/wiki/GettingStarted#Interface/Abstract_classes_support
Why not using jackson json library ?
It is a full Object/JSON Mapper with data binding functionnality.
It is fast, small footprint, documented, overused, and many others things you will enjoy!
I began a library that implements the desired fonctionality (for json and xml) if the json is encoded by the same library :
https://github.com/giraudsa/serialisation
to use it,
MyObject myObject = new SpecialisedObject();
String json = JsonMarshaller.ToJson(myObject);
MyObject myClonedObject = JsonUnMarshaller(json);

Categories

Resources