I am receiving following response and am trying to to parse it using JAXB but following code returns error below.
Exception in thread "main" org.springframework.http.converter.HttpMessageConversionException: Could not instantiate JAXBContext for class [class com.easytobook.EasytobookResponse]: 1 counts of IllegalAnnotationExceptions; nested exception is com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
com.easytobook.Roomtype#totalTaxesAndFees has mutually exclusive annotations #javax.xml.bind.annotation.XmlAttribute and #javax.xml.bind.annotation.XmlElement
this problem is related to the following location:
at #javax.xml.bind.annotation.XmlAttribute(name=EUR, required=false, namespace=##default)
I know exception is related to totalTaxsesAndFees but I am not sure how to solve the issue.
EasytobookResponse response = restTemplate.postForObject(
url, easy, EasytobookResponse.class);
System.err.println("RESPONSE >>>>"
+ response.getResponse().getHotelInfo().get(0).getId());
#XmlRootElement(name = "EasytobookResponse")
#XmlAccessorType(XmlAccessType.FIELD)
public class EasytobookResponse {
#XmlAttribute(name = "target")
private String target;
#XmlAttribute(name = "username")
private String username;
#XmlElement(name = "Response")
private Response response;
getters and setters
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElementWrapper(name="Hotelinfo")
#XmlElement(name="Hotel")
private List<Hotel> hotelInfo;
getters and setters
#XmlRootElement(name = "Hotel")
#XmlAccessorType(XmlAccessType.FIELD)
public class Hotel {
#XmlElement(name = "Id")
private int id;
#XmlElement(name = "FacilityGroups")
private FacilityGroups facilityGroups;
#XmlElement(name = "exact")
private int exact;
#XmlElementWrapper(name = "Roomtype")
#XmlElement(name = "Roomtype")
private List<Roomtype> roomType;
getters and setters
#XmlRootElement(name = "FacilityGroups")
#XmlAccessorType(XmlAccessType.FIELD)
public class FacilityGroups {
#XmlElement(name = "HandicapFacilities")
private int handicapFacilities;
#XmlElement(name = "HasInternet")
private int hasInternet;
#XmlElement(name = "HasParking")
private int hasParking;
#XmlElement(name = "PetsAllowed")
private int petsAllowed;
#XmlElement(name = "HasChildDiscount")
private int hasChildDiscount;
#XmlElement(name = "HasSwimmingPool")
private int hasSwimmingPool;
#XmlElement(name = "HasAirCon")
private int hasAirCon;
#XmlElement(name = "HasFitnessFacilities")
private int hasFitnessFacilities;
#XmlElement(name = "NonSmoking")
private int nonSmoking;
getters and setters
#XmlRootElement(name = "Roomtype")
#XmlAccessorType(XmlAccessType.FIELD)
public class Roomtype {
#XmlAttribute(name = "parentid")
private String parentid;
#XmlElement(name = "Roomid")
private long roomId;
#XmlElement(name = "Roomname")
private String roomName;
#XmlElement(name = "Capacity")
private int capacity;
#XmlElement(name = "Available")
private int available;
#XmlElement(name = "Totalrate")
private double totalRate;
#XmlAttribute(name="currency")
private String currency;
#XmlAttribute(name="EUR")
#XmlElement(name = "TotalTaxesAndFees")
private double totalTaxesAndFees;
#XmlElement(name = "TotalChargeable")
private double totalChargeable;
#XmlElement(name = "Reckratetotal")
private double reckRateTotal;
#XmlElement(name = "Breakfast")
private int breakfast;
#XmlElement(name = "Booklink")
private String bookLink;
#XmlElement(name = "Hoteldetailslink")
private String hotelDetailsLink;
#XmlElement(name = "Rtoken")
private String rToken;
#XmlElement(name = "Specialoffers")
private String specialOffers;
#XmlElement(name = "Bookingconditions")
private BookingConditions bookingConditions;
#XmlElement(name = "Rateinfo")
private RateInfo rateInfo;
getters and setters
#XmlRootElement(name = "Bookingconditions")
#XmlAccessorType(XmlAccessType.FIELD)
public class BookingConditions {
#XmlElement(name = "Bookable")
private int bookable;
#XmlElement(name = "Chargepoint")
private String chargePoint;
#XmlElement(name = "Requirements")
private Requirement requirement;
getters and setters
#XmlRootElement(name = "Requirements")
#XmlAccessorType(XmlAccessType.FIELD)
public class Requirement {
#XmlElement(name = "Capacity")
private int capacity;
#XmlElement(name = "Creditcardcvc")
private int creditCardCvc;
#XmlElement(name = "Billingaddress")
private int billingAddress;
getters and setters
#XmlRootElement(name = "Rateinfo")
#XmlAccessorType(XmlAccessType.FIELD)
public class RateInfo {
#XmlElement(name = "Underoccupancy")
private int underoccupancy;
#XmlElement(name = "Earlybooking")
private int earlyBooking;
#XmlElement(name = "Lastminutebooking")
private int lastMinuteBooking;
#XmlElement(name = "Nonrefundable")
private int nonRefundable;
#XmlElement(name = "Breakfastincluded")
private int breakfastIncluded;
getters and setters
Response
<?xml version="1.0" encoding="UTF-8"?>
<EasytobookResponse>
<Response target="test" username="project121">
<Hotelinfo>
<Hotel>
<Id>436924</Id>
<FacilityGroups>
<HandicapFacilities>0</HandicapFacilities>
<HasInternet>1</HasInternet>
<HasParking>1</HasParking>
<PetsAllowed>0</PetsAllowed>
<HasChildDiscount>0</HasChildDiscount>
<HasSwimmingPool>0</HasSwimmingPool>
<HasAirCon>1</HasAirCon>
<HasFitnessFacilities>0</HasFitnessFacilities>
<NonSmoking>1</NonSmoking>
</FacilityGroups>
<exact>436924</exact>
<Roomtype>
<Roomid parentid="2069840686">2069840686</Roomid>
<Roomname>Two Persons Standard + Breakfast</Roomname>
<Capacity>2</Capacity>
<Available>8</Available>
<Totalrate currency="AUD" EUR="1204.3398">1800</Totalrate>
<TotalTaxesAndFees currency="AUD" EUR="0">0
</TotalTaxesAndFees>
<TotalChargeable currency="AUD" EUR="1204.3398">1800
</TotalChargeable>
<Reckratetotal currency="AUD" EUR="1204.3398">1800
</Reckratetotal>
<Breakfast>1</Breakfast>
<Booklink>https://stage-site.easytobook.us/booking_screen.php?hotel_id=436924&exact=436924&city_id=23&arrival=20-11-2016&departure=29-11-2016¤cy=EUR&lang=en&room[2069840686]=1&persons=2&rooms=1&amu=280828207&utm_source=project121&utm_medium=affiliate&utm_term=Sydney&utm_content=etb5&utm_campaign=en&rtoken=1VSwnz7whjuxw-RGo5aP6cp-XmdDQSHC7twXmVrwPejOzYDFXdGY_bdbs9xbHuP8xU83qSmzJGz_vgQZHjeE5kVENJkuPBAtuKd6jTqIWCk,
</Booklink>
<Hoteldetailslink>http://stage-site.easytobook.us/hotel_proxy.php?hotel_id=436924&lang=en&arrival=20-11-2016&departure=29-11-2016¤cy=EUR&prs_arr%5B0%5D=2&amu=280828207&utm_source=project121&utm_medium=affiliate&utm_term=Sydney&utm_content=etb5&utm_campaign=en&rtoken=1VSwnz7whjuxw-RGo5aP6cp-XmdDQSHC7twXmVrwPejOzYDFXdGY_bdbs9xbHuP8xU83qSmzJGz_vgQZHjeE5kVENJkuPBAtuKd6jTqIWCk,
</Hoteldetailslink>
<Rtoken>1VSwnz7whjuxw-RGo5aP6cp-XmdDQSHC7twXmVrwPejOzYDFXdGY_bdbs9xbHuP8xU83qSmzJGz_vgQZHjeE5kVENJkuPBAtuKd6jTqIWCk,
</Rtoken>
<Specialoffers />
<Bookingconditions>
<Bookable>1</Bookable>
<Chargepoint>hotel</Chargepoint>
<Requirements>
<Capacity>1</Capacity>
<Creditcardcvc>1</Creditcardcvc>
<Billingaddress>1</Billingaddress>
</Requirements>
</Bookingconditions>
<Rateinfo>
<Underoccupancy>0</Underoccupancy>
<Earlybooking>0</Earlybooking>
<Lastminutebooking>0</Lastminutebooking>
<Nonrefundable>0</Nonrefundable>
<Breakfastincluded>1</Breakfastincluded>
</Rateinfo>
</Roomtype>
<Roomtype>
<Roomid parentid="1379794752">1379794752</Roomid>
<Roomname>Two Persons Superior + Breakfast</Roomname>
<Capacity>2</Capacity>
<Available>4</Available>
<Totalrate currency="AUD" EUR="1264.5568">1890</Totalrate>
<TotalTaxesAndFees currency="AUD" EUR="0">0
</TotalTaxesAndFees>
<TotalChargeable currency="AUD" EUR="1264.5568">1890
</TotalChargeable>
<Reckratetotal currency="AUD" EUR="1264.5568">1890
</Reckratetotal>
<Breakfast>1</Breakfast>
<Booklink>https://stage-site.easytobook.us/booking_screen.php?hotel_id=436924&exact=436924&city_id=23&arrival=20-11-2016&departure=29-11-2016¤cy=EUR&lang=en&room[1379794752]=1&persons=2&rooms=1&amu=280828207&utm_source=project121&utm_medium=affiliate&utm_term=Sydney&utm_content=etb5&utm_campaign=en&rtoken=ocGkvI7xuJJwz1BWWYNBztr7n-__tI8fVNfz3cZsrwRMGrtuHAEGziCH-0poK2ZoveEs-4Fz1_Y4U8pwE4KGKjJc4iwdSKM4ewIJwMU8omA,
</Booklink>
<Hoteldetailslink>http://stage-site.easytobook.us/hotel_proxy.php?hotel_id=436924&lang=en&arrival=20-11-2016&departure=29-11-2016¤cy=EUR&prs_arr%5B0%5D=2&amu=280828207&utm_source=project121&utm_medium=affiliate&utm_term=Sydney&utm_content=etb5&utm_campaign=en&rtoken=ocGkvI7xuJJwz1BWWYNBztr7n-__tI8fVNfz3cZsrwRMGrtuHAEGziCH-0poK2ZoveEs-4Fz1_Y4U8pwE4KGKjJc4iwdSKM4ewIJwMU8omA,
</Hoteldetailslink>
<Rtoken>ocGkvI7xuJJwz1BWWYNBztr7n-__tI8fVNfz3cZsrwRMGrtuHAEGziCH-0poK2ZoveEs-4Fz1_Y4U8pwE4KGKjJc4iwdSKM4ewIJwMU8omA,
</Rtoken>
<Specialoffers />
<Bookingconditions>
<Bookable>1</Bookable>
<Chargepoint>hotel</Chargepoint>
<Requirements>
<Capacity>1</Capacity>
<Creditcardcvc>1</Creditcardcvc>
<Billingaddress>1</Billingaddress>
</Requirements>
</Bookingconditions>
<Rateinfo>
<Underoccupancy>0</Underoccupancy>
<Earlybooking>0</Earlybooking>
<Lastminutebooking>0</Lastminutebooking>
<Nonrefundable>0</Nonrefundable>
<Breakfastincluded>1</Breakfastincluded>
</Rateinfo>
</Roomtype>
</Hotel>
.....
The error message says it all:
com.easytobook.Roomtype#totalTaxesAndFees has mutually exclusive annotations #javax.xml.bind.annotation.XmlAttribute and #javax.xml.bind.annotation.XmlElement
Your code has:
#XmlRootElement(name = "Roomtype")
#XmlAccessorType(XmlAccessType.FIELD)
public class Roomtype {
. . .
#XmlAttribute(name="EUR")
#XmlElement(name = "TotalTaxesAndFees")
private double totalTaxesAndFees;
That is not allowed.
Your problem is these XML elements:
<Roomtype>
<Roomid parentid="2069840686">2069840686</Roomid>
. . .
<Totalrate currency="AUD" EUR="1204.3398">1800</Totalrate>
<TotalTaxesAndFees currency="AUD" EUR="0">0</TotalTaxesAndFees>
<TotalChargeable currency="AUD" EUR="1204.3398">1800</TotalChargeable>
<Reckratetotal currency="AUD" EUR="1204.3398">1800</Reckratetotal>
For example, the following field annotation tells JAXB that parentid is an attribute of Roomtype, when you want it as an attribute of Roomid:
public class Roomtype {
#XmlAttribute(name = "parentid")
private String parentid;
Instead, in order to have an attribute on the <Roomid> element, you need a Roomid class (and you don't need the #XmlRootElement):
#XmlAccessorType(XmlAccessType.FIELD)
public class Roomid {
#XmlAttribute(name = "parentid")
private String parentid;
#XmlValue
private long roomId;
}
Your 4 total elements have the same structure, so they can share a single class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Total {
#XmlAttribute(name = "currency")
private String currency;
#XmlAttribute(name = "EUR")
private double eur;
#XmlValue
private double total;
}
These two classes are then used like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Roomtype {
#XmlElement(name = "Roomid")
private Roomid roomid;
. . .
#XmlElement(name = "Totalrate")
private Total totalrate;
#XmlElement(name = "TotalTaxesAndFees")
private Total totalTaxesAndFees;
#XmlElement(name = "TotalChargeable")
private Total totalChargeable;
#XmlElement(name = "Reckratetotal")
private Total reckratetotal;
Related
In the below code, I am expecting the attributes in the order of properties defined, but not able to get it. The order is always mixed up.
Parent Tag
#JacksonXmlRootElement
public class ParentSpec{
#JacksonXmlProperty(localName = "parentSpec")
#JacksonXmlElementWrapper
public MySpec mySpec;
}
ChildTag
public class MySpec {
#JacksonXmlProperty(localName = "mySpec")
#JacksonXmlElementWrapper(useWrapping = false)
public List<MyData> myDataList;
}
Error Child
#JsonPropertyOrder({"id", "type", "name", "data"})
public class MyData{
#JacksonXmlProperty(isAttribute = true)
public String id;
#JacksonXmlProperty(isAttribute = true)
public String type;
#JacksonXmlProperty(isAttribute = true)
public String name;
#JacksonXmlProperty(isAttribute = true)
public String data;
//Other Pojo
}
I tried using the #JsonPropertyOrder, but it works only for elements it seems.
I'm developing an SOAP client and I'm looking for more sophisticated solution to marshalling objects into XML-string using Jaxb2 library.
The goal is to marshall an object, which acts as a wrapper for any-type element. Example:
<Action id="5">
<Employee id="10">
<Name>John</Name>
</Employee>
</Action>
or.
<Action id="5">
<Department id="ABC">
<Name>Economy Department</Name>
<ParentId>CDE</ParentId>
</Department>
</Action>
Note: The xml root (Action) contains either "Employee" or "Department" or anything else.
My current working solution is as follows:
#XmlRootElement(name = "Action")
abstract class Action {
#XmlAttribute(name = "id")
protected String id;
}
class EmployeeAction extends Action {
#XmlElement(name = "Employee")
protected Employee employee;
}
class DepartmentAction extends Action {
#XmlElement(name = "Department")
protected Department department;
}
This works fine, but I'm looking for more universal solution, without the need to create class for each type (*Action extends Action). Name of the element must always be the same as the className of the (dynamic) type. My idea is something like this:
public class Action<T> {
#XmlAttribute(name = "id")
protected String id;
#XmlElement(name = "getClass().getSimpleName()") //???
protected T element;
}
... and marshalling something like:
Action<?> action = ...;
JAXBContext context = JAXBContext.newInstance(Action.class, action.getElement().getClass());
Marshaller marshaller = context.createMarshaller();
try(ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
marshaller.marshal(action, outStream);
return outStream.toString();
}
Is something like this possible?
Thanks in advance.
You can do something like this for the above-provided XML:
METHOD-1
Action.class;
#XmlRootElement(name = "Action")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Action {
#XmlAttribute(name = "id")
protected String id;
#XmlElements({
#XmlElement(name = "Employee", type = Employee.class),
#XmlElement(name = "Department", type = Department .class),
})
private ActionItem type;
}
ActionItem.class;
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class ActionItem {
#XmlElement(name = "Name")
protected String name;
}
Employee.class;
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Employee extends ActionItem {
#XmlAttribute(name = "id")
private String id;
}
Department.class;
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Department extends ActionItem {
#XmlAttribute
private String id;
#XmlElement(name = "ParentId")
private String parentID;
}
Main.class:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("action.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Action.class).createUnmarshaller();
final Action action = unmarshaller.unmarshal(xmlStreamReader, Action.class).getValue();
System.out.println(action.toString());
Marshaller marshaller = JAXBContext.newInstance(Action.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(action, System.out);
}
}
If you provided Employee XML then it produces the following result:
Action(id=5, type=Employee(id=10))
<Action id="5">
<Employee id="10">
<Name>John</Name>
</Employee>
</Action>
If you provide the Department XML then it produces the following result:
Action(id=5, type=Department(parentID=CDE))
<Action id="5">
<Department>
<Name>Economy Department</Name>
<ParentId>CDE</ParentId>
</Department>
</Action>
METHOD-2
Creating the interface and using it:
public interface ActionItem2 {
}
Action.class uses the created interface.
#XmlRootElement(name = "Action")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Action {
#XmlAttribute(name = "id")
protected String id;
#XmlElements({
#XmlElement(name = "Employee", type = Employee.class),
#XmlElement(name = "Department", type = Department .class),
})
private ActionItem2 type;
}
Employee.class which implements the created interface
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Employee implements ActionItem2 {
#XmlAttribute(name = "id")
private String id;
#XmlElement(name = "Name")
protected String name;
}
Department.class which implements the created interface
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Department implements ActionItem2 {
#XmlAttribute
private String id;
#XmlElement(name = "ParentId")
private String parentID;
#XmlElement(name = "Name")
protected String name;
}
Main.class(No changes)
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("action.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Action.class).createUnmarshaller();
final Action action = unmarshaller.unmarshal(xmlStreamReader, Action.class).getValue();
System.out.println(action.toString());
Marshaller marshaller = JAXBContext.newInstance(Action.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(action, System.out);
}
}
The result would be the same.
Method - 3
If you don't want to modify your POJO then you can do something like this:
#XmlRootElement(name = "Action")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Action {
#XmlAttribute(name = "id")
protected String id;
#XmlElements({
#XmlElement(name = "Employee", type = Employee.class),
#XmlElement(name = "Department", type = Department .class),
})
private Object type;
}
Employee.class:
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Employee {
#XmlAttribute(name = "id")
private String id;
#XmlElement(name = "Name")
protected String name;
}
Department.class:
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Department {
#XmlAttribute
private String id;
#XmlElement(name = "ParentId")
private String parentID;
#XmlElement(name = "Name")
protected String name;
}
This will provide the same output.
I have an Abstract class used as MappedSuperclass like :
#MappedSuperclass
#IdClass(LogId.class)
public abstract class Log {
private LocalDateTime moment;
private String pid;
// attributes omitted
private String type;
private String message;
#Id
public LocalDateTime getMoment() { return moment; }
#Id
public String getPid() { return pid; }
// getters/setters omitted
}
My Log class provides to every entity the same structure with standard attributes type and message. Each entity represents a table in my database which share every attributes (#Id and // omitted ones). Columns type and message might have different names & types.
My first attempt was :
#Entity
#Table(name = "flow_catcher")
public class FlowCatcher extends Log {
#Override
#Column(name = "message_type")
public String getType() {
return super.getType();
}
#Override
#Column(name = "count")
#Convert(converter = StringToIntegerDbConverter.class)
public String getMessage() {
return super.getMessage();
}
}
Following this post I used #AttributeOverride like :
#Entity
#Table(name = "flow_catcher")
#AttributeOverride(name = "type", column = #Column(name = "message_type"))
#AttributeOverride(name = "message", column = #Column(name = "count"))
public class FlowCatcher extends Log {
}
But it seems impossible to use any converter to get data count as a String and not an Integer.
Here is my error log:
Unable to build Hibernate SessionFactory; nested exception is
org.hibernate.tool.schema.spi.SchemaManagementException:
Schema-validation: wrong column type encountered in column [count] in table [flow_catcher];
found [int (Types#INTEGER)], but expecting [varchar(255) (Types#VARCHAR)]
Is there any way to get my column count as a String and not an Integer ?
PS: Hibernate hbm2ddl.auto is set on validate and database scheme can't change.
EDIT 1: I found a working solution but it uses another (unused) column in my database other_string_column which doesn't sound clean to me :
#Entity
#Table(name = "flow_catcher")
#AttributeOverride(name = "type", column = #Column(name = "message_type"))
#AttributeOverride(name = "message", column = #Column(name = "other_string_column"))
public class FlowCatcher extends Log{
private Integer count;
#Override
public String getMessage() {
return this.count.toString();
}
public Integer getCount() { return count; }
public void setCount(Integer count) { this.count = count; }
}
You can go with generic
#MappedSuperclass
#IdClass(LogId.class)
public class Log<T extends Comparable> implements Serializable {
#Id
private LocalDateTime moment;
#Id
private String pid;
//additional required fields here
// Do NOT SPECIFY MAPPING
private T message;
}
#Entity
#Table(name = "flow_catcher")
#AttributeOverride(name = "type", column = #Column(name = "message_type"))
#AttributeOverride(name = "message", column = #Column(name = "count", columnDefinition = "BIGINT(15)"))
public class FlowCatcher extends Log<Integer> {
private static final long serialVersionUID = -3629698185247120860L;
}
#Entity
#Table(name = "flow_catcher_another_sample")
#AttributeOverride(name = "type", column = #Column(name = "message_type"))
#AttributeOverride(name = "message", column = #Column(name = "message", columnDefinition = "VARCHAR(20)"))
public class FlowCatcherString extends Log<String> {
private static final long serialVersionUID = -3629698185247120860L;
}
I have stored jsonb object in postgresql like
{"sample": {"lastName": "Sahani", "firstName": "Sanjay"}, "address": "Address2", "bedrooms": 2, "postcode": "40 BS", "propertyType": "Type 2"}
My table name is valuation_report_json
and jsonb column name is params
I can get address using Specification but unable to get sample's firstName
My specification is
public class ValuationReportJSONSpecification implements Specification<ValuationReportJSON>{
private String locale;
private String fieldToSearch;
private String localeParameter;
public ValuationReportJSONSpecification(String locale, String fieldToSearch,String localeParameter) {
this.locale = locale;
this.fieldToSearch = fieldToSearch;
this.localeParameter=localeParameter;
}
#Override
public Predicate toPredicate(Root<ValuationReportJSON> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// TODO Auto-generated method stub
System.err.println("INSIDE");
// return cb.equal(cb.function("jsonb_extract_path_text", String.class, root.<String>get("params"), cb.literal(this.locale)), this.fieldToSearch);
return cb.equal(cb.function("jsonb_extract_path_text", String.class,root.<String>get("params"), cb.literal(this.locale),cb.literal(this.localeParameter)),this.fieldToSearch);
}
}
My models are
ValuationReportJSON :
#Data
#NoArgsConstructor
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#Table(name = "valuation_report_json")
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class ValuationReportJSON implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Type(type = "jsonb")
#Column(name = "parameters", nullable = false,columnDefinition = "jsonb")
private Params params;
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "property_id", nullable = true)
private Property property;
#Column(name = "entry_id", nullable = true)
private Integer entryId;
#Column(name = "report_data", nullable = true, columnDefinition = "text")
private String reportData;
#Column(name = "entry_date", nullable = true)
#CreationTimestamp
private Timestamp entryDate;
#Column(name = "modified_date", nullable = true)
#UpdateTimestamp
private Timestamp modifiedDate;
//getter setter
}
Params
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Params implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
public String address;
private String postcode;
private SampleObj sample;
private int bedrooms;
//getter setter
}
SampleObj:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class SampleObj implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
//getter setter
}
I cannot use join in predicate as my objects are not having mapping or not an entity.
because i have seen same question but objects were entity :Specification/Predicate to Search Nested Objects
I am strucked with this issue. I have created a POJO for nested JSON and I am getting data in MarketPrice object where marketPrices is an ArrayList which has two elements.
This is MarketPrice POJO class and actually I need to save it into the MarketPrice table. I.e, entire JSON object. But I have two entities. How can this be possible?
MarketPrice.java
#Entity
#Table(name = "MarketPrice")
public class MarketPrice {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "itemId")
private Long itemId;
#Column(name = "analysisDate")
private Date analysisDate;
#Column(name = "marketName")
private String marketName;
#Column(name = "category")
private String category;
#Column(name = "marketPlace")
private String marketPlace;
#Column(name = "state")
private String state;
#Column(name = "district")
private String district;
public ArrayList<Items> marketPrices;
Items.java
public class Items implements Serializable {
private static final long serialVersionUID = -2428562977284114465L;
#Id
#Column(name="id")
private int id;
#Column(name = "itemName")
private String itemName;
#Column(name = "unitofPrice")
private String unitofPrice;
#Column(name = "minimumPrice",columnDefinition = "Float(10,2)")
private Float minimumPrice;
#Column(name = "maximumPrice",columnDefinition = "Float(10,2)")
private Float maximumPrice;
This is my nested JSON data I'm getting from at the server side in the controller:
JSON data in marketPrices
{
"marketPrices": [{
"itemName": "Mango",
"unitofPrice": "Kg",
"minimumPrice": "10",
"maximumPrice": "20"
}, {
"itemName": "Grapes",
"unitofPrice": "Kg",
"minimumPrice": "30",
"maximumPrice": "40"
}],
"state": "xyz",
"district": 4,
"marketPlace": 5001,
"marketName": "pmc",
"category": "Fruits"
}
Controller.java
#RequestMapping(value = {"/saveAnalysis"} , method = RequestMethod.POST,consumes = "application/json")
#ResponseBody
public MarketPrice bulkSaveMarketAnalysis(#RequestBody
String marketPrices, HttpServletResponse response,
HttpServletRequest request) throws JsonProcessingException, IOException, JSONException{
MarketPrice marketPrice1 = new MarketPrice();
System.out.println("Json Data"+marketPrices);//here am getting valid nested json from UI
Gson gson = new Gson();
MarketPrice marketPrice = gson.fromJson(marketPrices, MarketPrice.class);//converting it into Entity type all values are present in it.
//Am strucked after this,How to save nested json into DB.
String marketDataResponse = analyserService.saveListOfMarketPrice(marketPrice);
marketPrice1.setStatusMessage("success");
return marketPrice1;
}
DAO.java
public String saveListOfMarketPrice(MarketPrice marketPrice) {
System.out.println("In Analyser DAO fro bulk saving");
final Session session = getSession();
session.beginTransaction();
marketPrice.setAnalysisDate(new Date());
for (Items item : marketPrice.marketPrices) {
marketPrice.currentItem = item;
marketPrice.setItemName(marketPrice.currentItem.getItemName());
marketPrice.setUnitofPrice(marketPrice.currentItem.getUnitofPrice());
marketPrice.setMinimumPrice(marketPrice.currentItem.getMinimumPrice());
marketPrice.setMaximumPrice(marketPrice.currentItem.getMaximumPrice());
session.save(marketPrice);
}
session.getTransaction().commit();
session.close();
return "success";
}
After making these changes to DAO it saved finally
Thank you.
As discussed in the comments, you can modify your code as below to make it work as expected.
MarketPrice.java
#Entity
#Table(name = "MarketPrice")
public class MarketPrice {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "itemId")
private Long itemId;
#Column(name = "analysisDate")
private Date analysisDate;
#Column(name = "marketName")
private String marketName;
#Column(name = "category")
private String category;
#Column(name = "marketPlace")
private String marketPlace;
#Column(name = "state")
private String state;
#Column(name = "district")
private String district;
#Transient
public Items currentItem;
#Column(name = "itemName")
public String getItemName() {
return this.currentItem.itemName;
}
#Column(name = "unitofPrice")
public String getUnitofPrice() {
return this.currentItem.unitofPrice;
}
#Column(name = "minimumPrice",columnDefinition = "Float(10,2)")
public Float getMinimumPrice() {
return this.currentItem.minimumPrice;
}
#Column(name = "maximumPrice",columnDefinition = "Float(10,2)")
public Float getMaximumPrice() {
return this.currentItem.maximumPrice;
}
#Transient
public ArrayList<Items> marketPrices;
Items.java
public class Items implements Serializable {
private static final long serialVersionUID = -2428562977284114465L;
#Id
#Column(name="id")
private int id;
public String itemName;
public String unitofPrice;
public Float minimumPrice;
public Float maximumPrice;
DAO.java
public String saveListOfMarketPrice(MarketPrice marketPrice) {
System.out.println("In Analyser DAO fro bulk saving");
final Session session = getSession();
session.beginTransaction();
for (Items item : marketPrice.marketPrices) {
marketPrice.currentItem = item;
session.save(marketPrice);
}
session.getTransaction().commit();
session.close();
return "success";
}
You should create one bigger entity for the whole Json ant it will have a list of you marketPrice objects #onetomany. Read about that annotation. And also parse your whole incoming object at once not only the list of marketPrices inside. You can read how to parse your whole object here: GSON parsing without a lot of classes
You need something like:
JsonObject rootObj = parser.parse(json).getAsJsonObject();
Then you should describe that structure in an entity:
#Entity
#Data
#Table(name = "your_table")
public class YourEntity{
//you should describe your parameters here too
//of the parsed json. it has other data in it not only the list of `MarketPrices`...
#OneToMany(mappedBy = "yourEntity",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY, orphanRemoval = true)
private List<MarketPrice> prices;
.....