In JavaScript we can do:
function foo() {
...
return {
attr1 : ... ,
attr2 : ...,
};
}
But what is its equivalent in Java?
Because I want to return a custom Json object from my controller after an ajax call and I want to create a new bean.
As Java dictates, you should create a new class and convert it to JSON. Also, you can use Map<String,Object> to accomplish the same thing. To generate following JSON:
{
"attr1":1,
"attr2":2
}
you can use following code:
Map<String,Object> map = new HashMap<>(3);
map.put("attr1", 1);
map.put("attr2", 2);
and convert it to JSON.
P.S.: HashMap in Java causes your heap to increase and unnecessary garbage, so I specified just enough size to keep two elements.
Related
can you tell me if exist some pretty way to map one entity object to another dto object? When I needed convert List<ObjEntity> to List<ObjDTO> I created that function:
public class Converter {
public static Function<ObjEntity, ObjDTO> MYOBJ_ENTITY_TO_DTO = objEntity -> {
ObjDTO dto = new ObjDTO();
dto.setId(objEntity.getId());
dto.setName(objEntity.getName());
dto.setNote(objEntity.getNote());
// ...
return dto;
};
}
and I use it like this:
List<ObjDTO> dtos = objEntitiesList.stream().map(Converter.MYOBJ_ENTITY_TO_DTO).collect(Collectors.toList());
But what if I need convert just ONE object? Should I use that function MYOBJ_ENTITY_TO_DTO for that and how? Or what is the best practice? Yes, I can do classical function in Converter class like that:
public static ObjEntity dtoToEntity(ObjDTO dto) {
// map here entity to dto and return entity
}
but it is old style. Exist some new practice in java 8? Something similar like my example for list by lambda?
ObjDTO dto = MYOBJ_ENTITY_TO_DTO.apply(entity);
I see the other way around more often: instead of MYOBJ_ENTITY_TO_DTO, define entityToDto as a method and use
List<ObjDTO> dtos = objEntitiesList.stream().map(Converter::entityToDto).collect(Collectors.toList());
for lists.
The code that we already have return us JsonObject. What I want to do is to add a new key and the value for it.
For example, we have an object like this:
{"id":"12","name":"test"}
I want to transform it into this:
{"id":"12","name":"test","status":"complete"}
I didn't find what I need in documentation except using put method. So I wrote this code:
JsonObject object = getJsonObject();
JsonString val = new JsonString() {
public JsonValue.ValueType getValueType() {
return JsonValue.ValueType.STRING;
}
public String getString() {
return "complete";
}
public CharSequence getChars() {
return (CharSequence) "complete";
}
};
object.put("status", val);
But it doesn't work, crashing with :
java.lang.UnsupportedOperationException
I can't understand what is wrong. Have I any other option to complete such a task?
I don't think JsonObject instances are meant to be modified.
I think your best option is to create a new object, copy the existing properties and add the new property to it.
You can use https://docs.oracle.com/javaee/7/api/javax/json/JsonObjectBuilder.html
Not sure if object.put works but you can use the following way to append the details to JSON value:
You can create a different JSON object with the key and value that you want to add to the JSON object and the user object.merge(status, complete, String::concat);
merge() checks for the key:'status' in your JSON object if it does'nt find it then it adds that key:value pair or else it replaces it
.You are not able to compile it because you may not be using jre 1.8.
I've Just verified the following method:
Just create a new JSONObject(org.json.JSONObject not javax.json.JsonObject)
JSONObject modifiedJsonObject= new JSONObject(object.toString());
modifiedJsonObject.put("status", "complete");
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
}
An API my application is communicating with sends responses that look like:
{
Code: 200,
Message: "HELLO",
Data: []
}
The Data field is always an array of SOMETHING. But that something could be a single node of text, another array of something else, or any other of an assortment of different objects.
In the below example, the data node is an array of an array of car objects.
Data: [ [ {car:1}, {car:2} ] ]
Another return type could be an array of insect objects:
Data: [ {insect : spider} ]
I would like to design a Gson object to handle this and was wondering what the best way would be.
My first thought is to have an abstract class that holds the Code and Message fields, and then have many sub-types that all have their own Data field. Then I would just call .fromJson() passing it the sub-class.
Is there a more optimal way to design it so that Gson would handle the differences?
I figured out what I believe is the best answer. Fairly straightforward!
Make the class generic and supply the type by creating a TypeToken before passing to Gson:
public class Response<T> {
private String code;
private String message;
private List<T> data;
}
Then when using Gson:
Type myCarListResponse = new TypeToken<Response<List<Car>>>(){}.getType();
Response<List<Car>> response = gson.fromJson(json, myCarListResponse);
Replace > with the type you are expecting from the Data node. The above example satisfies the first example from the original post.
To satisfy the second example:
Type myInsectResponse = new TypeToken<Response<Insect>>(){}.getType();
Response<Insect> response = gson.fromJson(json, myInsectResponse);
In Jackson, you can use #JsonAnyGetter/Setter to achieve this.
Refer http://www.cowtowncoder.com/blog/archives/2011/07/entry_458.html, http://wiki.fasterxml.com/JacksonFeatureAnyGetter
I'm currently trying to use Flexjson to deserialize a JSON String and map it to the Object model of my
Android App. The application is a kind of library with several vendors that can have some catalogs with more catalogs
and documents in them. The json is fetched from a web service I have no influence on and looks something like this:
{
"library":{
"vendor":[
{
"id":146,
"title":"Vendor1",
"catalog":[
{
"id":847,
"document":[
{
"id":1628,
"title":"Document",
...
},
{
...
}
],
"title":"Catalog ",
},
{
...
}
]
},
{
...
}
]
}
}
So each vendor, catalog, document is represented by a JSONObject and all child catalogues and documents are within a JSONArray.
So far everything works fine with Flexjson and the following deserialization code:
LibraryResponse response = new JSONDeserializer<LibraryResponse>()
.use(Timestamp.class, new TimestampObjectFactory())
.deserialize(getLocalLibrary(), LibraryResponse.class);
return response.library;
I do have a Library object that has a List<Vendor>. Each vendor has a List<Catalog> and a List<Document>.
But unfortunately, the web service straps the JSONArrays to simple JSONObjects if a catalog contains only a single document
or a catalog contains just one catalog. So the json in that case looks like this:
"document":
{
"id":1628,
"title":"Document",
...
}
Now Flexjson doesn't know how to deserialize and I end up with a library.vendorX.getDocument() being a List<HashMap> instead of a List<Document>.
One idea is to tell Flexjson explicitly how to handle such cases, but I have no idea where to start with this. Another way could be to parse the initial json manually and replace such JSONObjects with the appropriate JSONArray. But I think that way is not really nice to go, as the library can be pretty deep.
I hope you can provide some guidance here.
Yikes this is some gnarly json mapping going on. What backend coder did that?! #NotHelping.
Well from looking at the code, Flexjson is coded to handle this out of the box. But it looks like it's not passing the typing information down to the bind so it doesn't know what type it's binding into so it just returns a Map. That's a bug that should probably be fixed. Good news is there is a work around.
Anyway, the simplest thing I can think of is to install an ObjectFactory on that list. Then you can check and see if you get a Map or a List when the stream is deserialized. Then you can wrap it in a List and send it on to the appropriate decoder. Something like:
LibraryResponse response = new JSONDeserializer<LibraryResponse>()
.use(Timestamp.class, new TimestampObjectFactory())
.use("library.vendor.values.catalog.values.document", new ListDocumentFactory() )
.deserialize(getLocalLibrary(), LibraryResponse.class);
Then
public class ListDocumentFactory implements ObjectFactory {
public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass) {
if( value instanceof Collection ) {
return context.bindIntoCollection((Collection)value, new ArrayList(), targetType);
} else {
List collection = new ArrayList();
if( targetType instanceof ParameterizedType ) {
ParameterizedType ptype = (ParameterizedType) targetType;
collection.add( context.bind(value, ptype.getActualTypeArguments()[0]) );
} else {
collection.add( context.bind( value ) );
return collection;
}
}
}
}
I think that's roughly what would fix that bug, but should also fix your problem.