I'm using Jackson and want to serialize subclass' fields into subelement. Unfortunately Jackson has awful documentation.
#JsonRootName(value = "subclass")
public class ProfilerTask extends Task {
private int age;
private int grade;
public ProfilerTask(String name, Date createdOn, int age, int grade) {
super(name, createdOn);
this.age = age;
this.grade = grade;
}
/**
* #return the age
*/
public int getAge() {
return age;
}
/**
* #return the grade
*/
public int getGrade() {
return grade;
}
}
I'm getting this: {"name":"test task","createdOn":1372771395040,"age":25,"grade":4}, while I actually want subclass's fields to be a subelement.
I think, you should think about composition instead of inheritance. But if you really want to have inheritance you have to change POJO class. You can create new internal class and move all properties and fields into this new class. See my example:
public class ProfilerTask extends Task {
private Subclass subclass;
public ProfilerTask(String name, long createdOn, int age, int grade) {
super(name, createdOn);
this.subclass = new Subclass();
this.subclass.age = age;
this.subclass.grade = grade;
}
public Subclass getSubclass() {
return subclass;
}
#JsonIgnore
public int getAge() {
return subclass.age;
}
#JsonIgnore
public int getGrade() {
return subclass.grade;
}
public class Subclass {
private int age;
private int grade;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
}
Now, please see my simple main method:
ObjectMapper mapper = new ObjectMapper();
ProfilerTask task = new ProfilerTask("test task", 1372771395040L, 25, 4);
System.out.println(mapper.writeValueAsString(task));
This program prints:
{"name":"test task","createdOn":1372771395040,"subclass":{"age":25,"grade":4}}
I think this is the simplest way to create sub-element in JSON with Jackson.
Related
Keep getting Identifier Expected for no-argument constructor
I've tried adding String playerName; and so forth in the constructor to identify.
Also tried this.tp1.playerNamer, and taking away " = null / = 0"
but keep getting same message.
Rest of the code works fine if I take away the constructor
public class KosakowskiTennisPlayer2
{
private String playerName;
private String country;
private int rank;
private int age;
private int wins;
private int losses;
public void setPlayerName(String thePlayerName)
{ playerName = thePlayerName;}
public void setCountry(String theCountry)
{ country = theCountry;}
public void setRank(int theRank)
{ rank = theRank;}
public void setAge(int theAge)
{ age = theAge;}
public void setWins(int theWins)
{ wins = theWins;}
public void setLosses(int theLosses)
{ losses = theLosses;}
// accessor
public String getPlayerName()
{ return playerName;}
public String getCountry()
{ return country;}
public int getRank()
{ return rank;}
public int getAge()
{ return age;}
public int getWins()
{ return wins;}
public int getLosses()
{ return losses;}
KosakowskiTennisPlayer2 tp1 = new KosakowskiTennisPlayer2()
{
tp1.playerName;
tp1.country;
tp1.rank;
tp1.age;
tp1.wins;
tp1.losses;
}
Only with the constructor part is there a problem.
I am new to springboot, i am getting a response as below in my json response:
"Number": "08002050"
I have defined it as String in my spring boot app.
I want to get a response as below:
"Number": 08002050
How do i accomplish this. please help
You can manage it in server side with a tricky way.
public class User {
private int id;
private String name;
#JsonIgnore // ignore this field when serialize
private String number;
#JsonProperty(value = "number") // change name of field when serialize
private int intValueOfNumber;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getIntValueOfNumber() {
return Integer.parseInt(number); // parse number string to int
}
public void setIntValueOfNumber(int intValueOfNumber) {
this.intValueOfNumber = intValueOfNumber;
}
}
In this entity #JsonIgnore annotation is ignore your field for JSON serialization and pass intValueOfNumber as int to JSON. Your json will be following:
{"id":1,"name":"Java","number":44124}
You may lost zero suffix of number string when you parse it to int.
On the client app I have this POJO
public class Chicken {
private String name;
private int age;
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
and I run this
RestClient get = RestClient.create().method("GET")
.host("http://localhost:8080/DevCrowd")
.path("resources/chickens");
GluonObservableList<Chicken> sample = DataProvider.retrieveList(
get.createListDataReader(Chicken.class));
System.out.println(sample);
But I get the error:
WARNING: Failed to create object of type class com.devcrowd.test.Chicken from the following json object {"id":1,"name":"AAA","age":12}
java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
at java.lang.Class.newInstance(Class.java:427)
at com.gluonhq.connect.converter.JsonConverter.readFromJson(JsonConverter.java:111)
at com.gluonhq.connect.converter.JsonIterableInputConverter.next(JsonIterableInputConverter.java:108)
at com.gluonhq.connect.provider.DataProvider.lambda$retrieveList$21(DataProvider.java:194)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoSuchMethodException: com.gluonhq.notesapp.Chicken.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 6 more
On the server I have this entity:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
#Entity
#NamedQuery(name="all", query = "SELECT c FROM Chicken C")
public class Chicken {
#Id
#GeneratedValue
private long id;
private String name;
private int age;
public Chicken() {}
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
}
and this service:
#Path("chickens")
public class ChickensResource {
#Inject
ChickenService cs;
#GET
#Produces(MediaType.APPLICATION_JSON)
public String chickens() {
JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
List<Chicken> chickens = cs.getAllChickens();
chickens.stream().map(chicken -> Json.createObjectBuilder()
.add("name", chicken.getName())
.add("age", chicken.getAge())
.build())
.forEach(jsonArrayBuilder::add);
return jsonArrayBuilder.build().toString();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void save(JsonObject chicken) {
String name = chicken.getString("name");
int age = chicken.getInt("age");
cs.save(new Chicken(name, age));
}
}
I can POST correctly (I check the DB and what I POST is there so this is why the error stack has a Chicken object ready) but I can't read it back. Why is that?
As you can read in the docs for JsonConverter::readFromJson:
Convert the provided JSON Object into a Java object. If a new instance could not be created from the specified targetClass in the constructor, then null will be returned.
The conversion works by inspecting all the property methods of the target class. A property method is any field that has both a getter and a setter method.
Now if you check your exception:
java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
at java.lang.Class.newInstance(Class.java:427)
the reason becomes clear: the target class (com.gluonhq.notesapp.Chicken) can't be instantiated, because it looks for a parameterless constructor.
All you'll need to do is add one:
public class Chicken {
private String name;
private int age;
public Chicken() { }
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
...
}
EDIT
The DataProvider returns an observable list, and you should use the initializedProperty() to find out when the list is ready, so you can get its content:
RestClient get = RestClient.create().method("GET")
.host("http://localhost:8080/DevCrowd")
.path("/resources/chickens");
GluonObservableList<Chicken> sample = DataProvider.retrieveList(
get.createListDataReader(Chicken.class));
sample.initializedProperty().addListener((obs, ov, nv) -> {
if (nv) {
for (Chicken chicken : sample) {
System.out.println(chicken);
}
}
});
I have a CrudRepository with two entities.
Problem: The characteristic entity always creates an additional id field in the database but does not select the correct characteristic_id field to generate JSON.
machine entity
machine_id || name || description
characteristic entity
characteristic_id || machine_id || name || description || type || value
Question: Why does the characteristic entity always create an id field named id and does not use the definded filed characteristic_id?
JSON
How the entity manipulates the database layout:
Machine entity
#Entity
#Table(name = "maschine")
public class Machine {
private int machine_id;
private String name;
private String description;
private Set<Characteristic> characteristics;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "machine", cascade = CascadeType.ALL)
public Set<Characteristic> getCharacteristics() {
return characteristics;
}
public void setCharacteristics(Set<Characteristic> characteristics){
this.characteristics = characteristics;
}
public Machine(){}
public Machine(String name, String description){
this.name = name;
this.description = description;
}
#Override
public String toString() {
return "Machine [id=" + machine_id + ", name=" + name + ", description=" + description + "]";
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
public int getId() {
return machine_id;
}
public void setId(int machine_id) {
this.machine_id = machine_id;
}
#Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Characteristic entity
#Entity
#Table(name = "characteristic")
public class Characteristic {
private int characteristic_id;
private String name;
private String description;
private int type;
private int value;
private Machine machine;
#ManyToOne
#JoinColumn(name="machine_id")
public Machine getMachine(){
return machine;
}
public void setMachine(Machine machine){
this.machine = machine;
}
public Characteristic() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
public int getCharacteristic_Id() {
return characteristic_id;
}
public void setCharacteristic_Id(int characteristic_id) {
this.characteristic_id = characteristic_id;
}
#Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Column(name="type")
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
#Column(name="value")
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
#Override
public String toString() {
return "Characteristic [id=" + characteristic_id + ", name=" + name + ", description=" + description + ", type=" + type
+ ", value=" + value + "]";
}
}
My CrudRepository to interact with the database:
public interface MachineRepository extends CrudRepository<Machine, Integer>{
}
My Controller:
#RestController
public class HomeController {
private final MachineRepository machineRepository;
#Autowired
HomeController(MachineRepository machineRepository) {
this.machineRepository = machineRepository;
}
#RequestMapping(value = "/machine", method = RequestMethod.GET)
Collection<Machine> readMachines(){
return (Collection<Machine>) machineRepository.findAll();
}
}
This is all code I have written for the database operations..
Change to this code
#Id
#GeneratedValue
#Column(name="id", nullable = false)
public int getCharacteristic_id() {
return characteristic_id;
}
By default as you have not specified the column name in your configuration hibernate will use the name of the getter methode without "get" part as the column name in the table. Below is the configuration in your Characteristic Entity
#Id
#GeneratedValue
public int getId() {
return characteristic_id;
}
As you have not specified any explicit column name for the memeber characteristic_id , hibernate by default chooses Id (removing get from the getId() method) as the column name for the member. And hence it always creates a column named Id for you characteristics_id member .
In order to force hibernate to use characteristic_id as the column name , you can use #Column annotation and provide the column name as characteristics_id
apart from the #Id and #GeneratedValue you are using to mark the getter.
#Id
#GeneratedValue
#Column(name="characteristic_id")
public int getId() {
return characteristic_id;
}
or you can change the name of getter and setter as below
#Id
#GeneratedValue
public int getCharacterisitc_id() {
return characteristic_id;
}
public void setCharacterisitc_id(int characteristic_id) {
this.characteristic_id = characteristic_id;
}
How to add a calculated field to an entity?
DB table
table person {
id number,
first_name varchar,
last_name varchar,
...
}
Java entity
public class person {
BigDecimal id;
String firstName;
String lastName;
...//getters and setters
//what to add here???
public String getFullName() {
return firstName + " " + lastname;
}
}
I tried adding #Transient, but the field is ignored when converting to json.
I tried just leaving the method there, throws an exception that the setter is missing. adding the setter throws another exception that the field does not exist in the DB.
I tried adding #Transient and #JsonPropert, but the field is ignored when converting to json.
I tried adding #Formula, but hibernate (i think) says it is not implemented.
The idea is to have a simple calculated field that is ignored by jpa/hibernate but is used by jackson.
How can I accomplish this?
EDIT
Example full class
#Entity
#Table(name="FDF_PATIENT_COUNTIE")
#JsonIdentityInfo(generator = JSOGGenerator.class)
#JsonIgnoreProperties(ignoreUnknown = true)
#Audited
public class PatientCounty extends FgaBaseClass {
private static final long serialVersionUID = 1425318521043179798L;
private BigDecimal id;
private County FCounties;
private Patient patients;
public PatientCounty() {
}
public PatientCounty(County FCounties, Patient patients) {
this.FCounties = FCounties;
this.patients = patients;
}
#SequenceGenerator(name="generator", sequenceName="FDF_PATIENT_COUNTIE_SEQ")
#Id
#GeneratedValue(strategy=SEQUENCE, generator="generator")
#Column(name="ID", unique=true, nullable=false, precision=22, scale=0)
public BigDecimal getId() {
return this.id;
}
public void setId(BigDecimal id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_F_COUNTIE")
public County getFCounties() {
return this.FCounties;
}
public void setFCounties(County FCounties) {
this.FCounties = FCounties;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_FDF_PATIENT")
public Patient getPatients() {
return this.patients;
}
public void setPatients(Patient patients) {
this.patients = patients;
}
}