Using Spring MVC projections with JSON and Jackson - java

In the new version of Spring Data (Fowler), one can pass an interface to Spring MVC controller actions, and Spring Data will then automatically create a proxy implementation and bind values to this proxy object.
An example is given in the blog post that describes some of the new features in Spring Data Fowler:
interface Form {
#NotBlank String getName();
#NotBlank String getText();
}
#Controller
#RequestMapping(value = "/guestbook")
class GuestbookController {
#RequestMapping(method = RequestMethod.GET)
String guestbook(Form form, Model model) { ... }
#RequestMapping(method = RequestMethod.POST)
String guestbook(#Valid Form form, Errors errors, Model model) { ... }
}
My question is if this can also be done when deserializing JSON with Jackson? For instance, like this:
interface Form {
#NotBlank String getName();
#NotBlank String getText();
}
#Controller
#RequestMapping(value = "/guestbook")
class GuestbookController {
#RequestMapping(method = RequestMethod.POST)
String guestbook(#Valid #RequestBody Form form) { ... }
}
However, this gives the following exception:
Can not construct instance of Form, problem: abstract types either
need to be mapped to concrete types, have custom deserializer, or be
instantiated with additional type information
I understand what the problem is, but is there a solution that does not require me to create a class that implements my interface or write a lot of code? One that is simpler than this approach. Because otherwise I might as well go with a DTO approach, but I just found it to be super cool if I could simply use an interface as in the example.
I can use a DTO class just fine with the above approach (or avoid using JSON), but using an interface like in the blog post's example would be neat. But is this possible with the Jackson library when deserializing JSON?

You could use Jackson Mr Bean module, which does exactly what you want.
To use it, just download the specific dependency and set it as a module to the underlying ObjectMapper instance:
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
public class AbstractPojosExample {
public interface Person {
String getName();
int getAge();
Address getAddress();
default String asString() {
return String.format("%s, %d, %s", this.getName(), this.getAge(), this.getAddress().asString());
}
}
public interface Address {
String getStreet();
String getCity();
default String asString() {
return String.format("%s, %s", this.getStreet(), this.getCity());
}
}
private void run() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
String json = "[ { \"name\": \"John\", \"age\": 23, "
+ "\"address\": { \"street\": \"1001 5th Ave\", \"city\": \"New York\" } }, "
+ "{ \"name\": \"Jean Jacques\", \"age\": 26 , "
+ "\"address\": { \"street\": \"1001 Champs Elysees Blvd\", \"city\": \"Paris\" } }, "
+ "{ \"name\": \"Oscar Ruben\", \"age\": 54, "
+ "\"address\": { \"street\": \"1001 9th July Ave\", \"city\": \"Buenos Aires\" } } ]";
System.out.println(json);
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
people.stream().map(Person::asString).forEach(System.out::println);
}
public static void main(String[] args) throws IOException {
AbstractPojosExample example = new AbstractPojosExample();
example.run();
}
}
For the given json:
[
{
"name": "John",
"age": 23,
"address": {
"street": "1001 5th Ave",
"city": "New York"
}
},
{
"name": "Jean Jacques",
"age": 26,
"address": {
"street": "1001 Champs Elysees Blvd",
"city": "Paris"
}
},
{
"name": "Oscar Ruben",
"age": 54,
"address": {
"street": "1001 9th July Ave",
"city": "Buenos Aires"
}
}
]
The little program above produces the following output:
John, 23, 1001 5th Ave, New York
Jean Jacques, 26, 1001 Champs Elysees Blvd, Paris
Oscar Ruben, 54, 1001 9th July Ave, Buenos Aires

Related

Comparing two JSON files using java

I have two JSON files, called "pickevent1" and "pickevent2". I have to compare if both files are matching; if they don't match, I need to know where they don't match.
pickevent1
{
"pickEventActivities": [{
"orderId": "215",
"lineNbr": 0,
"pick": "EACH",
"activations": [{
"activationType": "Si",
"activationValue": "31"
}]
}]
}
pickevent2
{
"pickEventActivities": [{
"orderId": "115",
"lineNbr": 1,
"pick": "Hello",
"activations": [{
"activationType": "Bi",
"activationValue": "3"
}]
}]
}
I created a pick event POJO class:
#JsonRootName(value = "pickEventActivities")
#Data
#JsonPropertyOrder({ "orderId", "lineNbr", "pick"})
class PickEvent {
String orderId;
String lineNbr;
String pick;
List<Activation> activations;
}
and a Activation POJO class:
#Data
#JsonPropertyOrder({ "activationType", "activationValue"})
public class Activation {
String activationType;
String activationValue;
}
To make sure it works, I created a test class:
public void compareJson() throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
PickEvent result1 = objectMapper.readValue(new File("src/../pickevent1.json"), PickEvent.class);
PickEvent result2 = objectMapper.readValue(new File("src/../pickevent2.json"), PickEvent.class);
assertEquals(result1, result2);
}
But when I am doing assertSame(result1,result2) its giving me null for json values:
Exception in thread "main" java.lang.AssertionError: expected same:<PickEvent(orderId=null, lineNbr=null, pick=null, activations=null)> was not:<PickEvent(orderId=null, lineNbr=null, pick=null, activations=null)>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotSame(Assert.java:828)
at org.junit.Assert.assertSame(Assert.java:771)
at org.junit.Assert.assertSame(Assert.java:782)
at JsonDiff.PickEventDiff.comparejson(PickEventDiff.java:26)
at JsonDiff.PickEventDiff.main(PickEventDiff.java:32)
It should give me an assertion error, but the test succeeds.
It should give me an assertion error, but the test succeeds.
Because you use objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);. In fact, an exception occurred during the parsing process.
Try:
public void compareJson() throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
Wrapper wrapper = objectMapper.readValue(new File(""), Wrapper.class);
Wrapper wrapper2 = objectMapper.readValue(new File(""), Wrapper.class);
System.out.println(wrapper.equals(wrapper2));
}
#Data
static class Wrapper {
List<PickEvent> pickEventActivities;
}
You are trying to read a PickEvent Object but you're actually sending a list there.
Please change your json to
{
"pickEventActivities": {
"orderId": "115",
"lineNbr": 1,
"pick": "Hello",
"activations": [{
"activationType": "Bi",
"activationValue": "3"
}]
}
}
or try changing your code to
List<PickEvent> list1 = objectMapper.readValue(new File("src/../pickevent1.json"), new ParameterizedTypeReference<PickEvent>(){});
Here is my github repo for Intellij Idea plugin.
Basically JSON comparator implemented in java. It compares JSON by fields, values and objects in array

How do i add an attribute in an existing json file using java

I need to add a resource type attribute which takes dynamic value in the json file which i already have in my system .
I have tried writing the code but it's not working
This is the json i have (i have only shared first few lines)
{
"id": "example",
"text": {
"status": "generated",
And my required output after writing the code is
{
"resourceType": "Patient",
"id": "example",
"text": {
"status": "generated",
The resourceType field gets added and the value of the attribute we get is according to the requirement
You can use ObjectMapper to deserialise it in POJO, set the attribute and deserialise it back to String, e.g.:
class Example {
private String id;
private String resourceType;
private Text text;
//getters and setters
}
class Text {
private String status;
//getters and setters
}
public static void main(String[] args) throws Exception {
String string = "{\n" +
" \"id\": \"example\",\n" +
" \"text\": {\n" +
" \"status\": \"generated\""
+ "} "
+ "}";
ObjectMapper objectMapper = new ObjectMapper();
Example example = objectMapper.readValue(string, Example.class);
example.setResourceType("Patient");
System.out.println(objectMapper.writeValueAsString(example));
}
Here's the documentation for Jackson.

Gson howto parse different data types in a single array?

I have an API response that includes METAR weather data as well as string error responses, both contained in the same "data" array. I am using Gson to parse the json response on Android. This works great until I get a string error response. I've tried my hand at trying to write a custom Gson deserializer with no luck. Can someone give me a working example or point me in the correct direction on how to handle this?
The response looks like this:
{
"results": 4,
"data": [
{
"icao": "KAJO",
"name": "Corona Municipal",
"observed": "05-11-2018 # 18:56Z",
"raw_text": "KAJO 051856Z AUTO VRB03KT 10SM CLR 23/08 A2989 RMK AO2 SLP129 T02330078 $",
"barometer": {
"hg": 29.890000000000001,
"kpa": 101.22,
"mb": 1012.9
},
"clouds": [
{
"code": "CLR",
"text": "Clear skies",
"base_feet_agl": 0,
"base_meters_agl": 0
}
],
"dewpoint": {
"celsius": 8,
"fahrenheit": 46
},
"elevation": {
"feet": 535,
"meters": 163
},
"flight_category": "VFR",
"humidity_percent": 38,
"temperature": {
"celsius": 23,
"fahrenheit": 73
},
"visibility": {
"miles": "10",
"meters": "16,093"
},
"wind": {
"degrees": 0,
"speed_kts": 3,
"speed_mph": 3,
"speed_mps": 2
}
},
"KGNG METAR Currently Unavailable",
"CXCY Invalid Station ICAO"
]
}
As you can see the "data" array may return a metar object (I have this part working) or an unnamed error string. It is when I get the error string returned that my parsing fails.
As a test I wrote the following. But it is also not working. How can I parse the both the raw unnamed string and the metar object?
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.List;
public class Main {
public static void main(String[] args) {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Response.class, new MyDeserializer())
.registerTypeAdapter(String.class, new String())
.create();
Response response = gson.fromJson(str_json, Response.class);
System.out.println("Hello World!");
}
static class MyDeserializer implements JsonDeserializer<Response> {
#Override
public Response deserialize(JsonElement json, Type typeOfT
,JsonDeserializationContext context) throws JsonParseException {
// Get the "data" element from the parsed json
JsonElement data = json.getAsJsonObject().get("data ");
// Deserialize it. You use a new instance of Gson to avoid
// infinite recursion
return new Gson().fromJson(data, Response.class);
}
}
/*===============================
* Data Definitions
*==============================*/
class Response {
public String results;
public List<Station> Stations;
}
class Station {
public String name;
public String icao;
}
public static String str_json = "{\n" +
" \"results\": 3,\n" +
" \"data\": [\n" +
" {\n" +
" \"name\": \"Billings Logan Intl\"," +
" \"icao\":\"KBIL\"," +
" },\n" +
" \"CYPG METAR Currently Unavailable\",\n" +
" \"KGNG METAR Currently Unavailable\"\n" +
" ]\n" +
"}";
}
First it would make thing seasier if you changed the DTOs a bit, for the Response
public class Response {
public String results;
public List<Station> data; // it is named data in JSON not Stations
}
Then the rest is done depending on how you would like to handle the error text. One easy way would be to just add error field to your Station so that it would be:
public class Station {
public String name;
public String icao;
public String error; // used if there is only error string
}
With a custom deserialiser like:
public class StationDeserializer implements JsonDeserializer<Station> {
private final Gson gson = new Gson();
#Override
public Station deserialize(JsonElement json, Type typeOfT
,JsonDeserializationContext context)
throws JsonParseException {
try {
return gson.fromJson(json, Station.class);
} catch (JsonSyntaxException e) {
// it was not a Station object
Station station = new Station();
// so try to set the error string
station.error = json.getAsString();
return station;
}
}
}
The try to deserialize:
Response response = new GsonBuilder()
.registerTypeAdapter(Station.class, new StationDeserializer())
.create()
.fromJson(str_json, Response.class);
Checking if there is an error string in a Station or not you can see if the data is valid.

Spring HATEOAS add external content while deserializing JSON

I'm creating a client to query our JSON Api and I'm trying to extend a resource with the content coming from another resource. I want to do this because the resources are part of the same entity and our users will get back the single entity without the need to query the two services separately.
I'll put here a base version of the resources, to better explain the problem:
/* Sample class */
public class Sample {
public String accession;
public String name;
//...
public Map<RelationType, List<Relation>> relations
// ... classic getters and setters
}
// Relation class
public class Relation {
public String id;
// getters and setters
}
Now the JSON Api is something similar to this:
Here the Sample
{
"_embedded":{
"samples":[
{
"accession":"SAME1500861",
"name":"1380435",
"_links":{
"self":{ "href":"https://www.ebi.ac.uk/biosamples/api/samples/SAME1500861"
},
"sample":{
"href":"https://www.ebi.ac.uk/biosamples/api/samples/SAME1500861"
},
"relations":{
"href":"https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861"
}
}
}
]
},
"_links":{ }
}
Here the relations:
{
"accession": "SAME1500861",
"_links": {
"self": {
"href": "https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861"
},
"derivedFrom": {
"href": "https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861/derivedFrom"
},
"derivedTo": {
"href": "https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861/derivedTo"
},
"externalLinks": {
"href": "https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861/externalLinks"
},
"recuratedTo": {
"href": "https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861/recuratedTo"
}
}
}
Any suggestion would be great.
Thanks
You just need to create a Java class structure that represents the data in your JSON. You can use any of online viewers or some chrome extension to see stricture...
I've just done small part of your relations JSON...
public class Relations {
public String accession;
public Links _links;
}
public class Links {
public Self self;
}
public class Self {
public String href;
}
Here at least 2 option in my eyes...
Retrive JSON as string and convert via Gson
Use spring RestTemplate
Basically I would recommend to use Spring RestTemplate
RestTemplate restTemplate = new RestTemplate();
Relations result = restTemplate.getForObject("https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861", Relations.class);
Or you can use any lib to get JSON response and convert via Gson
String relationsPart = "{\n" + " \"accession\": \"SAME1500861\",\n" + " \"_links\": {\n" + " \"self\": {\n" + " \"href\": \"https://www.ebi.ac.uk/biosamples/api/samplesrelations/SAME1500861\"\n" + " }\n" + " }\n" + "}";
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Relations result = gson.fromJson(relationsPart, Relations.class);

Gson name array and include a new property

I'm starting to work with json and need some help figuring this out.
I have a pojo
public class HiReceiptsObj{
private string warehouse_id;
private String owner_id;
private String receipt;
.....// all with Getters and setters
}
I call a java method where I load an ArrayList of receipts like so
List<HiReceiptsObj> receipts = new ArrayList<HiReceiptsObj>();
and then I use gson to convert the ArrayList into json
String receiptsJson = new Gson().toJson(receipts);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(receiptsJson);
This will output
[{"warehouse_id":"US01","owner_id":"NP ","receipt":"INV"},
{"warehouse_id":"US01","owner_id":"NP ","receipt":"082212-3047 "},
{"warehouse_id":"US01","owner_id":"NP ","receipt":"INVADJUST"}]
I need this kind of output
{
"total_pages": 10,
"rows": [
{"warehouse_id":"US01","owner_id":"NP ","receipt":"INV"},
{"warehouse_id":"US01","owner_id":"NP ","receipt":"082212-3047 "},
{"warehouse_id":"US01","owner_id":"NP ","receipt":"INVADJUST"}
]
}
I have read other posts regarding this, but they are too advanced for me, can anybody please help?
Thanks
1) You should also have a holder class for your purpose:
public class ReceiptHolder {
#SerializedName("total_pages")
private int totalPages;
private List<HiReceiptsObj> rows;
// TODO: add getter / setters
}
2) Create receiptHolder instance and fill it:
List<HiReceiptsObj> receipts = new ArrayList<HiReceiptsObj>();
ReceiptHolder receiptHolder = new ReceiptHolder();
receiptHolder.setRows(receipts);
receiptHolder.setTotalPages(receipts.size());
3) Serialize receiptHolder:
String receiptsJson = new Gson().toJson(receiptHolder );
Output as you wanted:
{
"total_pages": 10,
"rows": [
{
"warehouse_id": "US01",
"owner_id": "NP ",
"receipt": "INV"
},
{
"warehouse_id": "US01",
"owner_id": "NP ",
"receipt": "082212-3047 "
},
{
"warehouse_id": "US01",
"owner_id": "NP ",
"receipt": "INVADJUST"
}
]
}

Categories