I have a Spring mvc application, with a #RestController like such:
#RestController
#RequestMapping("levels")
public class LevelController {
private final GetLevelOneCount getLevelOneCount;
private final GetLevelTwoCount getLevelTwoCount;
private final GetLevelThreeCount getLevelThreeCount;
#Inject
public LevelController(GetLevelOneCount getLevelOneCount,
GetLevelTwoCount getLevelTwoCount,
GetLevelThreeCount getLevelThreeCount) {
this.getLevelOneCount = getLevelOneCount;
this.getLevelTwoCount = getLevelTwoCount;
this.getLevelThreeCount = getLevelThreeCount;
}
#GetMapping("/level1/{id}")
public LevelModel levelOne(#PathVariable String id) throws SQLException {
LevelModel levelOneModel = new LevelModel();
levelOneModel.setLevelQuery(getLevelOneCount.execute(id));
levelOneModel.setLevelDirQuery(getLevelOneCount.executeDir(id));
levelOneModel.setLevelDateQuery(getLevelOneCount.executeDate(id));
return levelOneModel;
}
my LevelModel is a POJO with private variables, now i wonder, if this can get serialized to propper JSON with private variables?
package com.pwc.tag.service.levels;
public class LevelModel {
private Long LevelQuery;
private Long LevelDirQuery;
private Long LevelDateQuery;
public Long getLevelQuery() {
return LevelQuery;
}
public void setLevelQuery(Long levelQuery) {
LevelQuery = levelQuery;
}
public Long getLevelDirQuery() {
return LevelDirQuery;
}
public void setLevelDirQuery(Long levelDirQuery) {
LevelDirQuery = levelDirQuery;
}
public Long getLevelDateQuery() {
return LevelDateQuery;
}
public void setLevelDateQuery(Long levelDateQuery) {
LevelDateQuery = levelDateQuery;
}
}
Yes, your object will be serialized to a proper JSON structure including the private field, because of the getters and setters.
If these fields should not be present in the output object, you can add the #JsonIgnore annotation to exclude them from the JSON structure.
P.S. the common approach is to start names of java properties with a lower case letter.
Related
I would like to deserialize JSON of this structure:
{
"employee_pricing_type":"COMPUTE_BY_OWN_RATE",
"employee_rate":10,
"customer_pricing_type":"COMPUTE_BY_OWN_RATE",
"customer_rate":200
}
I have such POJO to create price setting from a HTTP request:
public class ObjectPricingSetting {
#JsonProperty("pricing_type") // describes output
private final ObjectPricingType pricingType;
#JsonProperty("own_rate") // describes output
private final BigDecimal ownRate;
public ObjectPricingSetting(final ObjectPricingType pricingType, final BigDecimal ownRate) {
AssertUtils.notNull(pricingType, "pricingType");
this.pricingType = pricingType;
if (ownRate != null) {
AssertUtils.isGtZero(ownRate, "ownRate");
this.ownRate = ownRate;
} else {
this.ownRate = null;
}
}
public ObjectPricingType getPricingType() {
return pricingType;
}
public BigDecimal getOwnRate() {
return ownRate;
}
}
this is DTO:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ObjectPricingCommand extends BaseDto<ObjectId> {
#JsonProperty(value = "employee_pricing_setting")
private ObjectPricingSetting employeePricingSetting;
#JsonProperty(value = "customer_pricing_setting")
private ObjectPricingSetting customerPricingSetting;
}
I would like to create these two instances of ObjectPricingSetting with #JsonCreator.
Q: How should I anotate #JsonProperty parameter in ObjectPricingSetting constructor to recognize what JSON value should use to create these two instances?
You can use #JsonUnwrapped with a prefix in your parent class:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ObjectPricingCommand extends BaseDto<ObjectId> {
#JsonUnwrapped(prefix = "employee_")
private ObjectPricingSetting employeePricingSetting;
#JsonUnwrapped(prefix = "customer_")
private ObjectPricingSetting customerPricingSetting;
}
Then you can use the normal #JsonCreator/#JsonProperty in your nested DTO, without the prefix:
public class ObjectPricingSetting {
#JsonCreator
public ObjectPricingSetting(
#JsonProperty("pricing_type") final ObjectPricingType pricingType,
#JsonProperty("rate") final BigDecimal ownRate) {
...
I have this classes structure to serialize TreeGrid (www.treegrid.com) object:
DataGrid.java
#XmlRootElement(name = "Grid")
public class DataGrid implements Serializable {
private static final long serialVersionUID = 337286974296229101L;
#XmlElement(name = "Body")
public DataGridData data = new DataGridData();
#XmlElement(name = "IO")
public XmlAttributeHolder io = new XmlAttributeHolder();
public DataGrid() {
}
}
and
DataGridData.java
public class DataGridData {
#XmlElement(name="B")
public DataGridCurrentPage currentPage = new DataGridCurrentPage();
#XmlElement(name="B")
public List<XmlAttributeHolder> pageList = new ArrayList<XmlAttributeHolder>();
}
These classes will be processed to return an XML structure as follow:
<Grid>
<Body>
<B />
</Body>
</Grid>
but the information encapsuled in B can be different (so exist two properties in DataGridData class mapped by the same XmlElement).
If I run my project under Java 7 that's all OK but with Java 8 is raise an exception about conflict two properties can't use the same XmlElement map.
A possible solution is: Encapsule two properties in two different classes as follow:
DataGridData.java
public class DataGridData {
private DataGridDataCP dataGridDataCP;
private DataGridDataPL dataGridDataPL;
public DataGridData() {
this.dataGridDataCP = new DataGridDataCP();
this.dataGridDataPL = new DataGridDataPL();
}
public DataGridDataCP getDataGridDataCP() {
return dataGridDataCP;
}
public void setDataGridDataCP(DataGridDataCP dataGridDataCP) {
this.dataGridDataCP = dataGridDataCP;
}
public DataGridDataPL getDataGridDataPL() {
return dataGridDataPL;
}
public void setDataGridDataPL(DataGridDataPL dataGridDataPL) {
this.dataGridDataPL = dataGridDataPL;
}
}
DataGridDataCP.java
public class DataGridDataCP {
private DataGridCurrentPage currentPage;
public DataGridDataCP() {
this.currentPage = new DataGridCurrentPage();
}
#XmlElement(name="B")
public DataGridCurrentPage getCurrentPage() {
return currentPage;
}
public void setCurrentPage(DataGridCurrentPage currentPage) {
this.currentPage = currentPage;
}
}
DataGridDataPL.java
public class DataGridDataPL {
private List<XmlAttributeHolder> pageList;
public DataGridDataPL() {
this.pageList = new ArrayList<XmlAttributeHolder>();
}
#XmlElement(name="B")
public List<XmlAttributeHolder> getPageList() {
return pageList;
}
public void setPageList(List<XmlAttributeHolder> pageList) {
this.pageList = pageList;
}
}
But in this way when DataGridData class has been serialized, add a tag <dataGridDataCP> (or <dataGridDataPL>) but I don't want to show this intermediate tag.
I've tried with XmlAccessorType annotation to exclude object DataGridDataCP / DataGridDataPL but this annotation exclude the complete object (with its encapsuled properties and no only the property in DataGridData class)
You can try something, but you will be able only to serialize properly. Deserialization will not work, and I will explain why. The code would look something like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class DataGridData {
#XmlElements({
#XmlElement(name = "B", type = DataGridCurrentPage.class),
#XmlElement(name = "B", type = XmlAttributeHolder.class),
})
List<Object> values;
}
Now if you create your POJOs and serialize you will get the XML you need. If this is your need then you should be fine.
But if you want also to deserialize, since both classes are mapped into #XmlElement with name 'B' it will pick one of the target classes for deserialization (I believe the last specified).
I have in my controller:
#RestController
public class OneTwoController {
private OnTwoService _service;
//... more code
#PostMapping("/api/one-two")
#CrossOrigin
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
return ResponseEntity.ok().body( _service.Save(model));
}
In my entity:
#Entity(name = "OneTwo")
#Where (clause = "deleted='false'")
public class OneTwo{
#EmbeddedId
private OneTwoKey_id;
public OneTwo(OneTwoKey id) {
this._id = id;
}
#JsonProperty("oneTwo")
public void setId(OneTwoKey value) {
this._id = value;
}
The OneTwoKey class:
public class OneTwoKey implements Serializable {
#Column(name = "OneID")
private int _oneID;
#Column(name = "TwoID")
private int _twoID;
public OneTwoKey(int oneID, int twoID) {
this._oneID = oneID;
this._twoID = twoID;
}
}
The json that I send to the Rest API:
{
"oneTwo": {
"oneID": 83,
"twoID": 69
},
"deleted": true
}
The issue is that both ids arrive null, so the service can't do the insert on the DB.
How can I deal with those cases when the ids are more than one?
Try adding setters in the OneTwoKey class to make it easier for the JSON deserializer:
#JsonProperty("oneID")
public void setOneID(int oneID) {
this._oneID = oneID;
}
#JsonProperty("twoID")
public void setTwoID(int twoID) {
this._twoID = twoID;
}
Another solution is to create a DTO, use it to receive the data in the controller and then convert it to your entity:
public class OneTwoDTO {
private Map<String, Int> oneTwo;
private boolean deleted;
// setters & getters
}
Simply what you can do is instead of using
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
you can use
public ResponseEntity<ServiceResponse> save(#RequestBody String model) {
Now convert the String to json and get all the key value pairs, it would be easier if you have dynamic number of variables and you want to capture them all.
or you can use tools like jsonschema2pojo whick take a json schema and generate a pojo. In the json schema if you set
"additionalProperties": true
you can capture all the values.
Could you make sure the problem is not because of case sensitivity?
Lower case the column names. Also could you use public access on those variables as well? These are my initial guesses as to why the payload is not being binded correctly.
public class OneTwoKey implements Serializable {
#Column(name = "oneID")
public int _oneID;
#Column(name = "twoID")
public int _twoID;
I have a Java class which has 2 List Object inside it and i am Json serializing the parent class.
#JsonSerialize
public class RequestSalesJson {
#JsonProperty("nonUniqueSalesList")
private List<SalesDataJson> getNonUniqueSalesDataJson;
#JsonProperty("uniqueSalesList")
private List<SalesDataJson> uniqueSalesDataJson;
public List<SalesDataJson> getGetNonUniqueSalesDataJson() {
return getNonUniqueSalesDataJson;
}
public void setGetNonUniqueSalesDataJson(List<SalesDataJson> getNonUniqueSalesDataJson) {
this.getNonUniqueSalesDataJson = getNonUniqueSalesDataJson;
}
public List<SalesDataJson> getUniqueSalesDataJson() {
return uniqueSalesDataJson;
}
public void setUniqueSalesDataJson(List<SalesDataJson> uniqueSalesDataJson) {
this.uniqueSalesDataJson = uniqueSalesDataJson;
}
}
SalesReturnJson.java
#JsonSerialize
public class SalesReturnJson {
#JsonProperty("starttime")
private String startTime;
#JsonProperty("pn")
private String partNumber;
#JsonProperty("so")
private String SalesOrderNumber;
#JsonProperty("wo")
private String workOrderNumber;
#JsonProperty("loc")
//other variables declared..
}
Controller.java :-
#RequestMapping(value = "/addAllSalesData",method = RequestMethod.POST)
public void addAllSalesData(#RequestBody RequestSalesJson requestSalesJsons){
log.info("POST : '/addSalesData'");
try{
System.out.print("In Controller "+requestSalesJsons.getUniqueSalesDataJson());
//salesService.processSalesData(requestSalesJsons);
}
catch(Exception e){
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
The value here is coming to be null.
Below is the json i am using :-
{ "uniqueSalesJson": [{"SO":4000955,"Part Number":"000","Locator":255638,"Lot Number":"P01-2059139","Reservation Quantity":2,"Status":"Released to warehouse","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"},{"SO":4000955,"Part Number":"000","Locator":255652,"Lot Number":"P01-2059140","Reservation Quantity":10,"Status":"Released to warehouse","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"}],"nonUniqueSalesJson":[{"SO":4000992,"Part Number":"1276M84G15","Locator":12345,"Lot Number":"P01-2344141","Reservation Quantity":6,"Status":"PACKED","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"},{"SO":4000992,"Part Number":"1276M84G15","Locator":12345,"Lot Number":"P01-2344141","Reservation Quantity":6,"Status":"PICKED","COE":"Fabrication","ORG":"P07","Start_Time":"2017-09-19 11:21:36"}]}
There are some issues in your code that let me doubt that your application compiles. First of all, rename the SalesReturnJson class to SalesDataJson.
Then check your #JsonProperty annotations. The value here must match exactly the property key in the Json String. Refactoring all this stuff will lead you to your root entity class:
#JsonSerialize
public class RequestSalesJson {
#JsonProperty("nonUniqueSalesJson")
private List<SalesDataJson> nonUniqueSalesDataJson;
#JsonProperty("uniqueSalesJson")
private List<SalesDataJson> uniqueSalesDataJson;
...
}
and your SalesDataJson class (missing a lot of attributes which the mapper ignores by configuration):
#JsonSerialize
public class SalesDataJson {
#JsonProperty("Start_Time")
private String startTime;
#JsonProperty("Part Number")
private String partNumber;
#JsonProperty("SO")
private String SalesOrderNumber;
}
This sample works as expected with the com.fasterxml.jackson.databind.ObjectMapper
Hope that helps!
In my project, I am having trouble writing a createCriteria query with a composite primary key. My Entity class & DAO method are given below -
#Entity
#Table(name="METRICS")
public class Metrics implements Serializable {
private static final long serialVersionUID = -2580493160757497919L;
#EmbeddedId
protected MetricsID metricsID;
#Column(name="PROJ_PERF")
private String proj_perf;
#Column(name="ANALYSIS")
private String analysis;
public String getProj_perf() {
return proj_perf;
}
public void setProj_perf(String proj_perf) {
this.proj_perf = proj_perf;
}
public String getAnalysis() {
return analysis;
}
public void setAnalysis(String analysis) {
this.analysis = analysis;
}
public MetricsID getMetricsID() {
return metricsID;
}
public void setMetricsID(MetricsID metricsID) {
this.metricsID = metricsID;
}
}
#Embeddable
public class MetricsID implements Serializable {
private static final long serialVersionUID = 4691163770334366543L;
#Column(name="PROJECT_ID")
private String project_id;
#Column(name="METRICS_NO")
private int metrics_no;
public String getProject_id() {
return project_id;
}
public void setProject_id(String project_id) {
this.project_id = project_id;
}
public int getMetrics_n0() {
return metrics_no;
}
public void setMetrics_no(int i) {
this.metrics_no = i;
}
}
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics = (List<Metrics>)sessionFactory.getCurrentSession().
createCriteria(Metrics.class).createAlias("metricsID.project_id", "project_id_alias").
add(Restrictions.eqProperty("project_id_alias.project_id", project_id)).list();
return metrics;
}
The error I am getting is - org.hibernate.QueryException: not an association: metricsID.project_id
I searched for several similar examples, and used alias on the suggestion of one of the search results, but it's my first time using an alias. What am I doing wrong?
Why do you need to use an alias? Have you tried to access directly?
Following this example, this code should work
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics =
(List<Metrics>) sessionFactory.getCurrentSession()
.createCriteria(Metrics.class)
.add(Restrictions.eq("metricsID.project_id", project_id))
.list();
return metrics;
}