I want to use Jackson to convert the Json payload of a HTTP request to a Java object.
However, I care only about some fields in that object.
e.g.
Obj
{
String a,
String b,
C c {
int d,
long e
}
}
I want Jackson to parse Json to a semi-populated Java object
e.g.
Obj
{
String a,
String b,
String c // as string: { int d,long e} }
}
Is this possible?
Looks like you are either looking for a propriety way of serializing and de-serializing property c from your example. For this purpose Jackson supplies the JsonSerialize and JsonDeserialize annotations. You can find more informations here.
Alternatively, you might want to ignore certain properties of your Java object during Jackson serialization. For this Jackson comes with a couple of options.
Related
I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}
Currently my applications can do as follows. It can read a JSON configuration such as:
{
"a": 5,
"b": 3
}
Into a POJO that looks like:
public class AddConf {
private Number a;
private Number b;
// constructor, getters and setters
public int add() {
return a.intValue() + b.intValue();
}
}
Then we can call the add function a return a result of 8 in this example. I would like to augment com.fasterxml.jackson.databind somehow to allow my JSON to have placeholders. So let's say when my configuration I don't know what value b will be until runtime, then I might make a configuration like this:
{
"a": 5,
"b": $b_placeholder$
}
To signify that b value will be provided at runtime. Obviously the above is not a valid JSON, and by default jackson throws an Exception (as it should) when it attempts to parse this with ObjectMapper's readValue. Ideally, I would like to read the above "JSON" (or something equivalent) into a POJO that looks something like this:
public class AddConf {
private Map<String,String> usedPlaceholders;
private Number a;
private Number b;
// constructor, getters and setters
public int add(Map<String,String> runtimeConf) {
if (usedPlaceholders.contains("a")) { // if "a" was a placeholder
a = runtimeConf.get(usedPlaceholder.get("a"));
}
if (usedPlaceholders.contains("b")) { // if "b" was a placeholder
b = runtimeConf.get(usedPlaceholder.get("b"));
}
return a.intValue() + b.intValue();
}
public void setPlaceholder(String key, String value) {
usedPlaceholder.put(key, value);
}
}
The idea is when deserializing the augmented JSON above, it would call setPlaceholder("b", "b_placeholder") instead of setting the value b and therefore when add is called, it will use values in the passed in runtimeConf Map instead of values from the JSON to do its configuration.
Given this, I have 2 questions:
Is there an easier way to accomplish my goal of having "placeholders" in my JSON configuration? It seems if I was to implement my idea here, I would have to override some of the Jackson classes. I would have to override the com.fasterxml.jackson.core.JsonParser to allow $ as a valid token in some scenarios, I would also have to write custom derserializers for all my configuration POJO (such as AddConf). This would likely casade into me having to override much of the jackson code base, which I would rather not do.
If I were to take this approach to override some of the default jackson classes, how might I go about doing that?
I am currently using jackson 2.6.0
You may be looking for annotation #JsonRawValue, use of which allows specifying EXACT contents to include as value, while still taking care of adding necessary separators.
Assume I have following DTO:
class C {
String a;
String b;
}
And I have the JSON:
{
"c" : {
"a" : "aaa",
"b" : "bbb"
}
}
What I want to do is, accomplish following test:
C expected = new C("aaa","bbb");
mockMvc.perform(get("url"))
.andExpect(jsonPath("$.c", is(expected)));
It fails. If I first serialize expected to JSON and then try to match, it again fails because it's a string. Is this possible?
Always remember: There is no such thing as a "JSON object". JSON is a serialization format for objects. JSON is always a string. You can convert from object to JSON and back (and hence from object to string and back). But
{ "a": "b" }
is a JavaScript object, not JSON (even if it looks very similar).
This in fact is the answer to your question: When you serialize expected, you get JSON (the transport format, i.e. the string). This isn't what jsonPath() checks. jsonPath() validates against JavaScript types.
This blog post suggests that you need to check each field individually:
.andExpect(jsonPath("$.c.a", is(expected.a)))
.andExpect(jsonPath("$.c.b", is(expected.b)));
which is tedious. What you need is
a) to configure your JSON framework to use a mapping system that sorts keys and
b) you need to figure out what type jsonPath("$.c", ...) returns - it's probably the type which your JSON framework uses to represent generic JavaScript objects.
The check then looks like this:
C c = new C("aaa","bbb");
String serialized = JSON.serialize(c); // to string
JSObject expected = JSON.parse(serialized); // to generic JavaScript object
mockMvc.perform(get("url"))
.andExpect(jsonPath("$.c", is(expected)));
Note that this only works if JSObject has a proper implementation for equals().
If you can afford to modify your "C" class to add it an "equals" operator and to modify slightly your JSON file, I would suggest you to transform your JSON string into an instance of "C". This can be done with a good JSON-ifier (Jackson or GSON). Then you just have to compare the 2 instances.
Some examples with GSON:
class C {
String a;
String b;
public boolean equals(C obj) { return a.equals(obj.a) && b.equals(obj.b); }
}
// Your JSON file should look like that
{
"a" : "aaa",
"b" : "bbb"
}
// So the test is simple
C expected = new C("aaa","bbb");
C json = gson.fromJson(jsonString, C.class);
if (expected.equals(json)) {
// Do whatever you want here
}
If you cannot afford to change the JSON file, just create another class to contains your main class, like this:
class Wrapper {
C c;
}
Wrapper jsonW = gson.fromJson(jsonString, Wrapper.class);
C json = jsonW.c;
...
If you cannot afford the addition of the equals operator, I suggest to create JSON string based on the 2 "C" instance objects and compare the strings. Your jsonString becomes a real "C" object (json) before ending into a new string (jsonStr).
String expectedStr = gson.toJson(expected);
String jsonStr = gson.toJSON(json);
if (expectedStr.equals(jsonStr)) {
// Do whatever you want here
}
class A{}
class B{}
class C{
private Map<A,B> myMap;
}
class Test{
public static void main(String [] args)
{
Map classMap=new HashMap();
classMap.put("myMap","?");
C c = (C) JSONObject.toBean(jsonObject, C.class,classMap);
}
}
I am using "net.sf.json" library for converting json object into java object.Here in class C there is a map, so how to convert it into Java Object. Here jsonObject is a json representation of class C.
My question is how to convert a json object into Java Object if java Object containing Map
I am a beginner, any help will be very thankful.
I have used jackson library and in that when a json is passed to the java code and if you want to parse that json into a java object you need to have a class which contains all the property which are present in the json string.
for example:
jsonString=
{
'firstname':'json',
'lastname':'jack'
}
will be equivalent to a java class which contains both the property as
class A {
String firstname;
String lastname;
}
so if you accept the string from frontend as the object of class A it works
like
public void (A objectofA){
}
and you call this method from front end and pass a json string using json library it will work and have faith in your work.
I don't know how to do it in "net.sf.json"
Try to see how serialization works in "net.sf.json". Probably, you can go from there.
This is how it is done using jackson, (one of the commenters has tried to explain you an approach using this)
//you need to import:
//import org.codehaus.jackson.map.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
mapper.writeValueAsString(c) //--> This gives Json String
I don't think this can work, as JSON cannot represent arbitrary objects as keys in maps. In JSON keys have to be strings. So in your example Map would only work if A was String.
Then you could say:
classMap.put("myMap",B.class);
I have a model object which is initialized with default values. To refresh the content of object I call an web service and get the response and get the content from json object.
I want to check If json response contains the object or not. If it does then call the setter and set the data and if it doesn't then leave then don't set it. I have approx 300 fields in my object. How I can do it with less code. I am listing my current approach.
My Model object is like
public class MyObject {
private String str1 = "Initial Value1";
private String str2 = "Initial Value2";
public void setStr1(String str1)
{
this.str1 = str1;
}
public void setStr2(String str2)
{
this.str2 = str2;
}
public String getStr1(){
return str1;
}
public String getStr2(){
return str2;
}
}
my json response be like
{
"val_one":"New Value1",
"val_two":"New_value2"
}
Now at run time I need to set the value from json response
MyObject myObject = new MyObject();
if(jsonObject.has("val_one"));
myObject.setStr1(jsonObject.get("val_one"));
if(jsonObject.has("val_two"));
myObject.setStr2(jsonObject.get("val_two"));
Now how to do it in a better and efficient
If both sides are using JAVA then why not just use json-io. You can create an object as normal. ie
Animal a = new Aminmal() andimal.setName("bob");
Then use json-io to make it into json -- stream to where ever it needs to be... use json io to change back to object
This can be done using
JsonWriter.objectToJson(Object o);
JsonReader.jsonToJava(String json);
https://code.google.com/p/json-io/
json-io is also extremely light weight and quicker than most if not all other third party json library's that I have used.
That being said if you want to have more control on the output ie.. date conversions etc.. then look at GSON.
https://code.google.com/p/google-gson/
Another option, in addition to the other suggestions is gson. Here the link for gson information.
Essentially the idea with gson being that you define an object to represent the JSON structure that you are receiving. So somewhat like what you have now, you'd just need to change the object attributes to match the names of the JSON fields, ie 'val_one' and 'val_two'.
Then you just need to use gson to create the object from the JSON text, eg:
Gson gson = new GsonBuilder().create();
MyObject json = gson.fromJson(jsonStr, MyObject.class);
Why do you want to take of the object model mapping yourself? If you take spring then you can use the jackson mapper and have it all done for you.
If you don't want to use spring then you still can use jackson2 and let it handle the parsing:
http://wiki.fasterxml.com/JacksonRelease20