I'm developing a web service that relies on Jersey servlet, and uses Jersey json converter.
I have a PatientDTO that has a field java.util.Date birthDate
Here are my methods
#GET
#Path("/{param}")
#Produces(MediaType.APPLICATION_JSON)
public Response getPatient(#PathParam("param") String id) {
PatientDTO patientDTO;
PatientManager manager = new PatientManagerFacade();
patientDTO = manager.getPatientById(id);
if (patientDTO == null) {
return Response.status(204).build();
}
return Response.status(200).entity(patientDTO).build();
}
#POST
#Path("/add")
#Consumes(MediaType.APPLICATION_JSON)
public Response addPatient(PatientDTO patient) {
PatientManager manager = new PatientManagerFacade();
String id = manager.addPatient(patient);
return Response.status(200).entity(id).build();
}
The problem is the following
For my POST method I'm giving the following json, and it gets parsed just fine, the timespan gets casted to java.util.Date
{
"firstName": "John",
"lastName": "Smith",
"birthDate": 722725200000,
"gender": "MALE",
"age": 18
}
But when I'm calling the return method, it gives me the following json
{
"id": "NM-001",
"firstName": "John",
"lastName": "Smith",
"birthDate": "1992-11-26",
"gender": "MALE",
"age": 23
}
But I want the birthDate to be a timespan too. How do I achieve this? Any annotations telling actually how to parse the field, or something like that?
Thanks in advance
Related
I want to write a REST service with JAX-RS that consumes a list of modules in the form
[
{
"name": "IRGENDWAS.TLK",
"version": "020",
"bibliothek": "asdf"
},
{
"name": "IRGENDWAS2.TLK",
"version": "030",
"bibliothek": "asdf"
},
{
"name": "XIRGENDWAS2.TLK",
"version": "030",
"bibliothek": "asdf"
}
]
and checks their existence, returning something like
[
{
"name": "IRGENDWAS.TLK",
"version": "020",
"bibliothek": "asdf",
"existence": true
},
{
"name": "IRGENDWAS2.TLK",
"version": "030",
"bibliothek": "asdf",
"existence": true
},
{
"name": "XIRGENDWAS2.TLK",
"version": "030",
"bibliothek": "asdf",
"existence": false
}
]
My best try so fat looks like this
#POST
#Path("/bs2-existence-check")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public List<Bs2ModulExistence> bs2ExistenceCheck(String modulListe)
{
#SuppressWarnings("unchecked")
List<String> nameList = JsonPath.parse(modulListe)
.read("$.[*].name", List.class);
#SuppressWarnings("unchecked")
List<String> versionList = JsonPath.parse(modulListe)
.read("$.[*].version", List.class);
#SuppressWarnings("unchecked")
List<String> bibliothekList = JsonPath.parse(modulListe)
.read("$.[*].bibliothek", List.class);
List<Bs2ModulExistence> backList = new ArrayList<>();
for (int i = 0; i < nameList.size(); i++)
{
String name = nameList.get(i);
String version = versionList.get(i);
String bibliothek = bibliothekList.get(i);
boolean existence = checkExistence(name, version, bibliothek);
Bs2ModulExistence bs2ModulExistence =
new Bs2ModulExistence(name, version, bibliothek, existence);
backList.add(bs2ModulExistence);
}
return backList;
}
I guess that my usage of JsonPath is unnecessary here and I can just map this somehow directly. Furthermore, I don't know whether this should be a POST request.
Probably someone can tell me how to do this correctly.
in order for your service to automatically marshal and unmarshal Java Objets to and from Json you have to specify a special parameter to your Jersey serlvet configuration (obviously this will be in the web.xml file). This parameter is com.sun.jersey.api.json.POJOMappingFeature and will basically integrate Jersey with Jackson.
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Create a POJO Module with attributes "name","version","bibliothek", "existence"
and try change your method to :
#POST
#Path("/bs2-existence-check")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public List<Bs2ModulExistence> bs2ExistenceCheck(List<Module> modulListe)
after you can iterate through moduleListe to check an update existance
return moduleListe.stream().map(m -> {
boolean existence = checkExistence(m.name, m.version, m.bibliothek);
m.existance = existence;
return m;
}).collect(Collectors.toList());
I have an object nested inside another object in Json file. I want to map this object with fields to a Model class.
{
"code": 200,
"time": "2019-09-05T07:09:44.228+0000",
"data": {
"statuses": [
{
"statusType": "IN_PROGRESS",
"statusTimestamp":"019-09-05T17:04:54+1000"
},
{
"statusType": "SENT",
"statusTimestamp":"2019-09-05T21:04:55+1000"
},
{
"statusType": "OPENED",
"statusTimestamp":"2019-09-05T23:04:55+1000"
},
{
"statusType": "INTERACTION_ID_RECEIVED",
"statusTimestamp":"2019-09-06T00:04:55+1000"
}
]
},
"status": 200,
"message": null,
"errors": null,
}
I want to map the statusType and TimeStamp to a custom model class.
Model Class:
public class Model{
private String statusType;
private DateTime statusTimestamp;
public Model(String statusType, String statusTimestamp) {
this.statusType=statusType;
this.statusTimestamp=new DateTime(statusTimestamp);
}
public String getStatusType() {
return statusType;
}
public void setStatusType(String statusType) {
this.statusType = statusType;
}
public DateTime getStatusTimestamp() {
return statusTimestamp;
}
public void setStatusTimestamp(String statusTimestamp) {
this.statusTimestamp = new DateTime(statusTimestamp);
}
}
I want to map the statuses to this model class and store these objects in a link something like this
List statuses = ParsedJson.read("$..['statuses'][*]", List.class)
If you don't want to model the entire response, you could use Jackson to parse the JSON into tree nodes and then map only the parts you care about:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
JsonNode statusesNode = rootNode.path("data").path("statuses");
List<Status> statuses = mapper.convertValue(statusesNode,
new TypeReference<List<Status>>(){});
I saw 2 errors in the json string fixing it should help you to create Object using java or any other language easily.
line 12: " was missing
line 26: invalid placement of ,
Json after fixing issues
{
"code": 200,
"time": "2019-09-05T07:09:44.228+0000",
"data": {
"statuses": [
{
"statusType": "IN_PROGRESS",
"statusTimestamp":"019-09-05T17:04:54+1000"
},
{
"statusType": "SENT",
"statusTimestamp":"2019-09-05T21:04:55+1000"
},
{
"statusType": "OPENED",
"statusTimestamp":"2019-09-05T23:04:55+1000"
},
{
"statusType": "INTERACTION_ID_RECEIVED",
"statusTimestamp":"2019-09-06T00:04:55+1000"
}
]
},
"status": 200,
"message": null,
"errors": null
}
When using postman, i get the following error
Syntax error: unexpected 'S'
even though my JSON String seems fine. I have looked on similar topics, that typically says to give the response object an entity that can be properly converted to JSON. I have parsed a List of pets with GSON.
Code:
#Path("Pet")
public class PetResource {
#Context
private UriInfo context;
public PetResource() {
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getJson() {
PetMapper pm = new PetMapper();
JSONConverter jsonconv = new JSONConverter();
List<Pet> petList = pm.getPets();
String json = jsonconv.getJsonFromPets(petList);
return Response.ok().entity(json).build();
}
#PUT
#Path("/size")
#Consumes(MediaType.APPLICATION_JSON)
public Response getPetSize() {
PetMapper pm = new PetMapper();
return Response.ok(pm.getPetSize()).build();
}
}
GSon Converter
public class JSONConverter {
static Gson gson = new GsonBuilder().setPrettyPrinting().create();
public String getJsonFromPets(List<Pet> pets) {
String petsString = "";
for (Pet pet : pets) {
petsString += gson.toJson(pet) + " ";
}
return petsString;
}
}
The following is my Raw Response
{
"id": 1,
"name": "Fiddo",
"birth": "2015-02-01",
"species": "Dog",
"owner_id": 1,
"events": []
} {
"id": 2,
"name": "Hannibal",
"birth": "2013-05-10",
"species": "Dog",
"owner_id": 1,
"events": []
} {
"id": 3,
"name": "Elvis",
"birth": "2010-08-08",
"species": "Cat",
"owner_id": 3,
"events": []
} {
"id": 4,
"name": "Sam",
"birth": "2012-01-05",
"species": "Rabbit",
"death": "2015-07-07",
"owner_id": 2,
"events": []
}
Your output text is not valid JSON. The problem is with how the list is being serialized.
I believe it should be enough to give your JSON library the entire list:
public String getJsonFromPets(List<Pet> pets) {
return gson.toJson(pets);
}
I have an end-point which returns me this response:
{
"head": {
"status": 200,
"ok": true,
"messages": [],
"errors": [],
"references": {}
},
"body": {
"id": "d57a9c7aef9842c2e31a0f49c",
"flowId": "f57979d06f9842c3e94f1f197",
"creationDate": 1470744494732,
"path": "/luafanti/test",
"version": 0,
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
}
}
My question is, how to make a model that can be populated with only a part of my json response. For example, with this:
"xxx": {
"type": "integer",
"value": 200
}
or this:
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
Using Jackson, you can define your model as the following:
#JsonIgnoreProperties(ignoreUnknown=true)
public class MyResponseModel {
private Body body;
public void setBody(Body body) {this.body = body;}
public Body getBody() {return body;}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Body {
private Elems elems;
// getter and setter for elems
}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Elems {
private Xxx xxx;
// getter and setter for xxx
}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Xxx {
private String type;
private String value;
// getter and setter for type and value
}
}
The above is quite verbose, particularly if you are only interested in a very small part of the response. It may be more practical to handle the response as a String and then use e.g. JsonPath to extract only the data you are interested in.
You can use simple-json.jar to extract that object from inside the JSONObject
Downloadable Jar Link - simple-json.jar Download Link
Maven Jar Import Maven Repository pom syntax
You actual object is
{
"head": {
"status": 200,
"ok": true,
"messages": [],
"errors": [],
"references": {}
},
"body": {
"id": "d57a9c7aef9842c2e31a0f49c",
"flowId": "f57979d06f9842c3e94f1f197",
"creationDate": 1470744494732,
"path": "/luafanti/test",
"version": 0,
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
}
} // hold this complete object in any string reference Variable.
Here I Suppose String jsonString holds the complete json object as above described.
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.JSONObject;
// implemetation logic for extracting jsonObject.
private JSONObject extractFirstBlock(String jsonString) throws ......{
JSONObject jsonObj ;
JSONParser parser=new JSONParser(); // parser to parse string to JSONObject
jsonObj = (JSONObject) parser.parse(jsonString); // parse the Object using parse Method.
String desiredObject = (String) jsonObj.get("body"); // this Object contains your desired output which you wish to receive.
jsonObj = parser.parse(desiredObject);
desiredObject = jsonObj.get("elems"); // you will get your desired object as you expected.
}
Here in desiredObject you will get your expected Values as JSONObject.
"xxx": {
"type": "integer",
"value": 200
}
I've a CXF RESTful service which returns both XML and Json format. I need to add a custom http header in the RESTful service. Here's a sample code snippet.
#GET
#Path("/test")
#Produces("application/xml")
public Response test(
#QueryParam("p") String var
{
TestRequest req = new TestRequest();
req.setVar(var);
TestResponse res = p.getData(req);
return Response.ok(res).header("Result", res.getResult()).build();
}
The above code shows the XML response which sets the custom http header "Result". I'm able to see the new http header in the response header. So far so good.
Now, here's the Json version which internally calls the testService() method to get the result, then use google Gson API to send the result back. This has been working well, till I decided to return the new header. Here's the code snippet.
#GET
#Path("/test/jsonp")
public String testJSONP(
#QueryParam("p") String var,
#QueryParam("cb") String callBack
{
Response resp = test(var);
XStream xs = new XStream(new JsonHierarchicalStreamDriver());
xs.setMode(XStream.NO_REFERENCES);
xs.alias("TestResponse", TestResponse.class);
StringBuilder sb = new StringBuilder();
sb.append(callBack);
sb.append("(");
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(XMLGregorianCalendar.class, new XMLGregorianCalenderSerializer());
gb.setPrettyPrinting();
Gson gson = gb.create();
sb.append(gson.toJson(resp));
sb.append(")");
return sb.toString();
}
I'm not able to see the http header in Json response.
Any feedback will be highly appreciated.
-Thanks
UPDATE
I added the following code in Json method for my testing.
#GET
#Path("/test/jsonp")
public String testJSONP(
#QueryParam("p") String var,
#QueryParam("cb") String callBack
{
Response resp = test(var);
XStream xs = new XStream(new JsonHierarchicalStreamDriver());
xs.setMode(XStream.NO_REFERENCES);
xs.alias("TestResponse", TestResponse.class);
StringBuilder sb = new StringBuilder();
sb.append(callBack);
sb.append("(");
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(XMLGregorianCalendar.class, new XMLGregorianCalenderSerializer());
gb.setPrettyPrinting();
Gson gson = gb.create();
sb.append(gson.toJson(resp));
sb.append(")");
return Response.ok(sb.toString(), MediaType.APPLICATION_JSON).header("Result", "50").build();
}
This sets the header value correctly,but the issue is the Json response format seems to have changed. Since this is an existing service, I'm not allowed to do that.
Here's the existing response format
null({
"status": "Completed",
"totalResult": "252",
"bin": [
{
"type": "source",
"value": "documentation",
"ndocs": "243"
},
{
"type": "source",
"value": "wikihelp",
"ndocs": "6"
},
"entries": {
"item": [
{
"url": "http://test.com/test.htm",
"title": "\u003cspan class\u003d\"vivbold qt0\"\u003eXREF\u003c/span\u003e",
"snippet": " Test data.",
"source": "documentation",
"type": "html",
"shortDescription": "Starts the TEST command.",
"category": [
"User"
],
"publishDate": "2012-02-05T12:00:00-0500",
"lastUpdateDate": "2012-03-14T12:00:00-0400",
"topicId": "GUID-7DD70C3C-B8AD-40F1-8A69-5D1EECEAB013"
}
]
}
})
Here's the response after adding this change
null({
"status": 200,
"entity": {
"status": "Completed",
"totalResult": "252",
"bin": [
{
"type": "source",
"value": "documentation",
"ndocs": "243"
},
{
"type": "source",
"value": "wikihelp",
"ndocs": "6"
}
],
"entries": {
"item": [
{
"url": "http://test.com/test.htm",
"title": "\u003cspan class\u003d\"vivbold qt0\"\u003eXREF\u003c/span\u003e",
"snippet": " Test data.",
"source": "documentation",
"type": "html",
"shortDescription": "Starts the TEST command.",
"category": [
"User"
],
"publishDate": "2012-02-05T12:00:00-0800",
"lastUpdateDate": "2012-03-14T12:00:00-0700",
"topicId": "GUID-7DD70C3C-B8AD-40F1-8A69-5D1EECEAB013"
}
]
}
},
"metadata": {
"Result": {
}
}
})
You need to change signature of your method, to return an instance of Response class, instead of a String, and then built the response manually.
From the CXF wiki page:
#Path("/example")
public ExampleResource {
#GET
public Response getSomething() {
return Response.ok(/* some entity */).header("CustomHeader", "CustomValue").build();
}
}
Update
You can also inject HttpServletResponse into your handler using #Context annotation like this:
#Path("/example")
public class Welcome {
#GET
public String getSomething(
#QueryParam("p1") String param1,
#QueryParam("p2") String param2,
#Context HttpServletResponse response) {
response.addHeader("CustomHeader", "CustomValue");
return "my awesome response";
}
}
Note, that there is a CXF-1498 bug in versions prior to 2.1 that causes HttpServletResponse not being injected, so you need a newer CXF version.