Jackson JSON with list attribute - json

I have the following 2 classes in my application:
public class GolfCourse {
private int id;
#JsonBackReference
private List<Hole> holes;
......
}
public class Hole{
private int id;
#JsonManagedReference
private GolfCourse course;
......
}
When I try to serialize a list of GolfCourse objects as JSON using Jackson:
List<GolfCourse> courses
......(populate course)
String outputJSON = new ObjectMapper().writeValueAsString(golfCourses);
I end up with a JSON array that only shows the id attribute for each of the golf courses, but it does not include the hole list:
[{"id":"9ed243ec-2e10-4628-ad06-68aee751c7ea","name":"valhalla"}]
I have verified that the golf courses all have holes added.
Any idea what the issue may be?
Thanks

I managed to get desired result by using #JsonIdentityInfo annotation:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class GolfCourse
{
public int id;
public String name;
public List<Hole> holes;
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Hole
{
public int id;
public String name;
public GolfCourse course;
}
test method:
public static void main(String[] args) {
Hole h1 = new Hole();
Hole h2 = new Hole();
GolfCourse gc = new GolfCourse();
h1.id = 1;
h1.name = "hole1";
h1.course = gc;
h2.id = 2;
h2.name = "hole2";
h2.course = gc;
gc.id = 1;
gc.name = "course1";
gc.holes = new ArrayList<>();
gc.holes.add(h1);
gc.holes.add(h2);
ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValue(System.out, gc);
} catch (Exception e) {
e.printStackTrace();
}
}
output:
{"id":1,"name":"course1","holes":[{"id":1,"name":"hole1","course":1},{"id":2,"name":"hole2","course":1}]}

Related

How to use native query with spring repo mapped to custom object?

I have table t1:
id | title
1 | title1
2 | title2
and I have the following spring repo method:
#Query(nativeQuery = true, value = "select id, title from t1")
public List<T1> getAll();
The custom class is:
public class T1 {
#JsonProperty("id")
private Integer id;
#JsonProperty("title")
private String title;
public T1(Integer id, String title) {
this.id = id;
this.title = title;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
I'm expecting to get the following json response:
{[{"id":1, "title":"titl1"}, {"id":2, "title":"titl2"}]}
However i'm getting this one:
[[1,"title1"],[2,"title2"]]
I'm using #RestController
#RequestMapping(method = RequestMethod.GET, value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntityTestResponse> test() {
List<T1> list = testRepository.getAll();
TestResponse response = new TestResponse(list);
return new ResponseEntity<TestResponse>(response, HttpStatus.OK);
}
TestResponse class is:
public class TestResponse implements Serializable {
private TreeSet<T1> list = new TreeSet<>();
public TestResponse(TreeSet<T1> list) {
this.list = list;
}
....
Can you help with that?
This response is classic Java List, if you need it as JSON object, you have to use for example GSON and then you should write something like this:
sonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
System.out.println(gson.toJson(YOUR_LIST_OF_T1));
Here are examples (included that was i wrote) and here is GitHub repo.
You can do it manually by overrite toString() method in T1 class if u need it in specific signature, and u didn't got any API doing what you want.
So if u try some thing like that
List<T1> list = testRepository.getAll();
StringBuilder strBuilder = new StringBuilder();
for(T1 t : list){
strBuilder.append(t.toString() + ", ");
}
String result = "";
if(strBuilder.length()!=0){
result = "{[" + strBuilder.substring(0, strBuilder.length()-2) + "]}";
}
System.out.println(result);
and class should overrite toString() method
class T1{
#JsonProperty("id")
private Integer id;
#JsonProperty("title")
private String title;
public T1(Integer id, String title) {
this.id = id;
this.title = title;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Override
public String toString() {
return "{\"id:\"" + id + ", \"title:\"" + title + "}";
}
}
Also if you want to use pure java standard library you can do it like next code but you will need to download javax.json-xxxx.jar(for example >> javax.json-1.0.4.jar) (that include providers or the implementation) to your library project path
But this next code will generate something like that
[{"id":1,"title":"Title1"},{"id":2,"title":"Title2"},{"id":3,"title":"Title3"}]
List<T1> list = testRepository.getAll();
JsonArrayBuilder jsonArray = Json.createArrayBuilder();
for(T1 t : list) {
jsonArray.add(Json.createObjectBuilder()
.add("id", t.getId())
.add("title", t.getTitle()));
}
System.out.println(jsonArray.build());

Json data is not wrapped from RESTEasy services

I implements test RESTEasy services. These are simple codes.
Member.java
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "member")
public class Member implements Serializable{
#XmlElement(name = "id")
private String id;
#XmlElement(name = "passwd")
private String passwd;
#XmlElement(name = "age")
private int age;
#XmlElement(name = "name")
private String name;
public String getId() {
return id;
}....
REST Service java
#Path("/services")
public class RESTEasyServices {
#GET
#Path("getJson/{id}")
#Produces(MediaType.APPLICATION_JSON) // This config can't be understood
public Response sayJsonHello(#PathParam("id") String id){
Member m = new Member();
m.setId(id);
m.setPasswd("aaa");
m.setAge(45);
m.setName("joseph");
//return m;
return Response.status(200).entity(m).build();
}
}
Invocation URI is successful. But Json data is not wrapped:
{"id":"aupres","passwd":"aaa","age":45,"name":"joseph"}
However when I set the attribute of #Produce to MediaType.APPLICATION_XML like below,
#Produces(MediaType.APPLICATION_XML)
response of XML data is wrapped.
<member>
<id>aupres</id>
<passwd>aaa</passwd>
<age>45</age>
<name>joseph</name>
</member>
I have no idea how to wrap the json data.
This is solved. These are my codes
#GET
#Path("getJson/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Map<String, List<Member>> sayJsonHello(#PathParam("id") String id){
Member m1 = new Member();
m1.setId(id);
m1.setPasswd("aaa");
m1.setAge(45);
m1.setName("joseph");
Member m2 = new Member();
m2.setId("hwa5383");
m2.setPasswd("bbb");
m2.setAge(40);
m2.setName("jina");
List<Member> list = new ArrayList();
list.add(m1);
list.add(m2);
Map<String, List<Member>> mems = new HashMap();
mems.put("member", list);
return mems;
}
Then the output is Json type
{"member":[{"id":"aupres","passwd":"aaa","age":45,"name":"joseph"},{"id":"hwa5383","passwd":"bbb","age":40,"name":"jina"}]}
Thanks any way.

How to parse 2 entities as JSON for Controller (which have 2x#RequestBody parameters)

I'm wondering is it possible to parse 2 entities as one JSON for my Controller which do have 2x#RequestBody(for each entity),first i want to check if that is possible, because i would like to have one controller with 3x#RequestBody(3 entities).
Controller looks like this :
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<KoordynatorzyPraktykEntity> addCoordinator(#RequestBody KoordynatorzyPraktykEntity koordynatorzyPraktykEntity, #RequestBody OsobyEntity osobyEntity) {
KoordynatorzyPraktykEntity addCoordinator = ikoordynatorzyPraktykService.addCoordinator(koordynatorzyPraktykEntity);
OsobyEntity addPerson = ikoordynatorzyPraktykService.addPerson(osobyEntity, koordynatorzyPraktykEntity);
if (addCoordinator !=null && addPerson !=null) {
return new ResponseEntity<KoordynatorzyPraktykEntity>(addCoordinator, HttpStatus.OK);
}
else {
return new ResponseEntity<KoordynatorzyPraktykEntity>(HttpStatus.NOT_FOUND);
}
}
Service:
#Override
public OsobyEntity addPerson(OsobyEntity osobyEntity, KoordynatorzyPraktykEntity koordynatorzyPraktykEntity) {
OsobyEntity newPerson = iosobyDAO.addPerson(osobyEntity);
newPerson.setKoordynator(koordynatorzyPraktykEntity);
int idOsoby = newPerson.getIdOsoby();
koordynatorzyPraktykEntity.setIdOsoby(idOsoby);
koordynatorzyPraktykEntity.setKoordynatorByIdOsoby(newPerson);
return newPerson;
}
Osoby(person)Entity
#Entity
#Table(name = "osoby", schema = "public", catalog = "praktykidb")
public class OsobyEntity {
private int idOsoby;
/*
some stuff
*/
private KoordynatorzyPraktykEntity koordynator;
#Id
#GeneratedValue
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
}
/*
some stuff
*/
#OneToOne(mappedBy = "koordynatorByIdOsoby", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public KoordynatorzyPraktykEntity getKoordynator() {
return koordynator;
}
public void setKoordynator(KoordynatorzyPraktykEntity koordynator) {
this.koordynator = koordynator;
}
and Koordynatorzy(Coordinators)Entity:
#Entity
#Table(name = "koordynatorzy_praktyk", schema = "public", catalog = "praktykidb")
public class KoordynatorzyPraktykEntity {
private int idKoordynatoraPraktyk;
private int idOsoby;
private String doTestow;
private OsobyEntity koordynatorByIdOsoby;
private Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku;
#Id
#GeneratedValue
#Column(name = "id_koordynatora_praktyk")
public int getIdKoordynatoraPraktyk() {
return idKoordynatoraPraktyk;
}
public void setIdKoordynatoraPraktyk(int idKoordynatoraPraktyk) {
this.idKoordynatoraPraktyk = idKoordynatoraPraktyk;
}
#Basic
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
/*
some stuff
*/
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_osoby", referencedColumnName = "id_osoby", insertable = false , updatable = false)
public OsobyEntity getKoordynatorByIdOsoby() {
return koordynatorByIdOsoby;
}
public void setKoordynatorByIdOsoby(OsobyEntity koordynatorByIdOsoby) {
this.koordynatorByIdOsoby = koordynatorByIdOsoby;
}
#JsonIgnore
#OneToMany(mappedBy = "koordynatorzyByIdKierunku", cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
public Collection<KoordynatorzyKierunkowiEntity> getKoordynatorzyByIdKierunku() {
return koordynatorzyByIdKierunku;
}
public void setKoordynatorzyByIdKierunku(Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku) {
this.koordynatorzyByIdKierunku = koordynatorzyByIdKierunku;
}
I know i can easilly make Person in PersonEntity and attach coordinator to this {PersonID} with #RequestMapping (value = "/{PersonID}/coordinator, method = POST (parse CoordinatorEntity as JSON and set relations for each entity), but i would like to test if something simillar (with 2xrequestBody) can work because in near future (tomorrow maybe...) i'm going to make new stuff for existing relation which looks now like this now.
[StudentEntity] [AddressEntity]
idOfStudent (PK) /--------idOfAddress (PK)
idOfAddress (FK)<-----/
idOfPerson (FK)<-----\ [PersonEntity]
\--------idOfPerson (PK)
With 2 entity it's not a problem, but with 3 i don't know what to do because when i make Student it assigns ID of Address and Person as 0, then when i want to add for example Address i want to search (em.find) student with his id, but he is also looking for rows in Address and Person Entity with idOfAddress = 0 and idOfPerson = 0. Maybe there is a way to make these IDs null when i persist Student into entity? Some other ideas maybe?
This is a wrong way, because the request body can be consumed only once, all subsequent attempts to process the body again will cause an IOException saying stream closed.
What you should do instead is build and register a custom argument resolver for your types OsobyEntity and KoordynatorzyPraktykEntity and achieve the same effect.

JSON to POJO using Apache Camel and hibernate

Apache camel is using a route wich is listening to a specific url. the json from this url is then transformed to pojo classes and inserted in a mySQL database. Everything is working fine, except my foreign key still remains null. I'm using spring framework btw.
Here is the url where you can find the data:
https://builds.apache.org:443/job/Accumulo-1.5/api/json
Here is my routedefinition for camel
#Component
public class JenkinsConfigurationRouteBuilder extends SpringRouteBuilder {
private Logger logger = LoggerFactory.getLogger(JenkinsConfigurationRouteBuilder.class);
#Override
public void configure() throws Exception {
logger.info("Configuring route");
//Properties die hij niet vindt in de klasse negeren
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DataFormat jenkinsConfigFormat = new JacksonDataFormat(objectMapper, JenkinsConfiguration.class);
from("timer://foo?fixedRate=true&delay=0&period=200000&repeatCount=1")
.routeId("jsonToJenkinsConfiguration")
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.to("https://builds.apache.org:443/job/Accumulo-1.5/api/json")
.convertBodyTo(String.class)
.unmarshal(jenkinsConfigFormat) //instance van JenkinsConfiguration
.log(LoggingLevel.DEBUG, "be.kdg.teamf", "Project: ${body}")
.to("hibernate:be.kdg.teamf.model.JenkinsConfiguration");
}
}
My POJO class
#Entity(name = "jenkinsConfiguration")
public class JenkinsConfiguration extends Configuration implements Serializable {
#Column
#JsonProperty("displayName")
private String name;
#JsonProperty("healthReport")
#JsonIgnore
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = ("jenkinsConfig"))
private Collection<HealthReport> healthReport;
#JsonProperty("builds")
#JsonIgnore
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = ("jenkinsConfig"))
private Collection<Build> builds;
#JsonProperty("modules")
#JsonIgnore
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = ("jenkinsConfig"))
private Collection<Module> modules;
public JenkinsConfiguration() {
}
public JenkinsConfiguration(Collection<Build> builds, Collection<HealthReport> healthReport, Collection<Module> modules, String name) {
this.builds = builds;
this.healthReport = healthReport;
this.modules = modules;
this.name = name;
}
public Collection<Build> getBuilds() {
return builds;
}
public Collection<HealthReport> getHealthReport() {
return healthReport;
}
public Collection<Module> getModules() {
return modules;
}
public String getName() {
return name;
}
public void setBuilds(Collection<Build> builds) {
this.builds = builds;
}
public void setHealthReport(Collection<HealthReport> healthReport) {
this.healthReport = healthReport;
}
public void setModules(Collection<Module> modules) {
this.modules = modules;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
Let us take the builds for instance.
As you can see, this pojo class contains a list from builds. A JenkinsConfiguration can contain more builds. One build belongs to one JenkinsConfiguration.
This is my Build class:
#XmlRootElement(name = "builds")
#Entity(name = "build")
public class Build implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int Id;
#Column
#JsonProperty("number")
private Integer number;
#Column
#JsonProperty("url")
private String url;
#JsonBackReference
#OnDelete(action = OnDeleteAction.CASCADE)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "jenkinsConfig")
private JenkinsConfiguration jenkinsConfig;
public Build() {
}
public Build(JenkinsConfiguration jenkinsConfig, Integer number, String url) {
this.jenkinsConfig = jenkinsConfig;
this.number = number;
this.url = url;
}
public int getId() {
return Id;
}
public JenkinsConfiguration getJenkinsConfig() {
return jenkinsConfig;
}
public Integer getNumber() {
return number;
}
public String getUrl() {
return url;
}
public void setId(int id) {
Id = id;
}
public void setJenkinsConfig(JenkinsConfiguration jenkinsConfig) {
this.jenkinsConfig = jenkinsConfig;
}
public void setNumber(Integer number) {
this.number = number;
}
public void setUrl(String url) {
this.url = url;
}
}
My question: how come that my foreign key is not set for the build class? it remains null.
Doe I need to update it manually or something? If so, how do I do that in spring?
Any help would me much appreciated!
Fixed it by updating the records in my database like so:
Camel:
from("hibernate:be.kdg.teamf.model.Build?delay=1s")
.routeId("buildFkBuild")
.startupOrder(3)
.shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
.to("bean:buildFK?method=processBuild")
.log(LoggingLevel.DEBUG, "be.kdg.teamf", "Project: ${body}")
.to("hibernate:be.kdg.teamf.model.Build");
Bean:
#Consumed
public Build processBuild(Build build) {
//updaten van foreign key
build.setJenkinsConfig(jenkinsConfiguration);
return build;
}

JSON unmarshalling to POJO and inserting

I would like to unmarshal a json string to a pojo class.
I am reading it from an existing url:
https://builds.apache.org/job/Accumulo-1.5/api/json
I am using apache camel to unmarshal the url
#Component
public class RouteBuilder extends SpringRouteBuilder {
private Logger logger = LoggerFactory.getLogger(RouteBuilder.class);
#Override
public void configure() throws Exception {
logger.info("Configuring route");
//Properties die hij niet vindt in de klasse negeren
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DataFormat reportFormat = new JacksonDataFormat(objectMapper, HealthReport.class);
from("timer://foo?fixedRate=true&delay=0&period=2000&repeatCount=1")
.routeId("accumoloToJsonRoute")
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.to("https://builds.apache.org:443/job/Accumulo-1.5/api/json")
.convertBodyTo(String.class)
.unmarshal(reportFormat) //instance van Build
.log(LoggingLevel.DEBUG, "be.kdg.teamf", "Project: ${body}")
.to("hibernate:be.kdg.teamf.model.HealthReport");
}
}
So far so good. I would like to only insert the 'healthReport' node using hibernate annotations.
#XmlRootElement(name = "healthReport")
#JsonRootName(value = "healthReport")
#Entity(name = "healthreport")
public class HealthReport implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int Id;
#Column
#JsonProperty("description")
private String description;
#Column
#JsonProperty("iconUrl")
private String iconUrl;
#Column
#JsonProperty("score")
private int score;
public HealthReport() {
}
public HealthReport(int score, String iconUrl, String description) {
this.score = score;
this.iconUrl = iconUrl;
this.description = description;
}
public String getDescription() {
return description;
}
public String getIconUrl() {
return iconUrl;
}
public int getId() {
return Id;
}
public int getScore() {
return score;
}
public void setDescription(String description) {
this.description = description;
}
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public void setId(int id) {
Id = id;
}
public void setScore(int score) {
this.score = score;
}
}
This is where the problem is. It does not recognize the annotations
and only null values are inserted in my database
#XmlRootElement(name = "healthReport")
#JsonRootName(value = "healthReport")
Does anybody know how to fix this?
Thanks
Fixed it using a Processor for my Route
public class HealthReportProcessor implements Processor {
#Autowired
private ConfigurationService configurationService;
#Override
public void process(Exchange exchange) throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(exchange.getIn().getBody().toString());
ArrayNode report = (ArrayNode) root.get("healthReport");
int configId = configurationService.findJenkinsConfigurationByName(root.get("displayName").asText()).getId();
for (JsonNode node : report) {
JsonObject obj = new JsonObject();
obj.addProperty("description", node.get("description").asText());
obj.addProperty("iconUrl", node.get("iconUrl").asText());
obj.addProperty("score", node.get("score").asInt());
obj.addProperty("jenkinsConfig", configId);
exchange.getIn().setBody(obj.toString());
}
}
}
It is working but I think there is a better solution.
If you have a better solution please let me know ;)
Can you try this,
from("timer://foo?fixedRate=true&delay=0&period=2000&repeatCount=1")
.routeId("accumoloToJsonRoute")
.setHeader(Exchange.HTTP_METHOD,constant("GET"))
.to("https://builds.apache.org:443/job/Accumulo-1.5/apijson")
.unmarshal().json(JsonLibrary.Jackson, HealthReport.class)
And make sure the response params match the POJO fields.
Let me know if it works.