Could not write JSON: Could not set field value [t] by reflection - java

I'm working on a Spring Boot 2.0.5.RELEASE project.
I have a field in an Oracle database declared as CHAR(1) with a JPA converter as follows:
public class CharToBooleanConverter implements AttributeConverter<String, Boolean> {
#Override
public Boolean convertToDatabaseColumn(String s) {
return s.equalsIgnoreCase("t");
}
#Override
public String convertToEntityAttribute(Boolean aBoolean) {
if(aBoolean.equals(true)){
return "t";
} else {
return "f";
}
}
}
This converter is used in my StructureElement class twice:
#Entity
#Table(name = "OBS_STRUCTURE_ELEMENT2")
#SequenceGenerator(name = "structure_element_seq", sequenceName = "structure_element_seq", allocationSize = 1)
public class StructureElement {
#Id
#Column(name = "NO_ELEMENT")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "structure_element_seq")
private long id;
#Column(name = "TAG")
private String tag;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "SUITE")
private int sequence;
#Column(name = "OPTIONNEL")
#Convert(converter = CharToBooleanConverter.class)
private boolean optional;
#Column(name = "REPETITIF")
#Convert(converter = CharToBooleanConverter.class)
private boolean repetitive;
#ManyToOne
#JoinColumn(name = "NOM_STRUCTURE_TYPE")
private Structure typeStructure;
#Embedded
private PersistenceSignature signature;
}
The problem is that when I try to send a Structure through a RestController I receive the following in console:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Could not set field value [t] value by reflection : [class be.solodoukhin.domain.StructureElement.optional] setter of be.solodoukhin.domain.StructureElement.optional; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Could not set field value [t] value by reflection : [class be.solodoukhin.domain.StructureElement.optional] setter of be.solodoukhin.domain.StructureElement.optional (through reference chain: be.solodoukhin.domain.Structure["elements"])]
With an 500 Internal Server Error response.
Here is my method:
#RestController
#RequestMapping("/structure")
public class StructuresController {
#GetMapping("/{name}")
public ResponseEntity<Structure> getOne(#PathVariable("name") String name)
{
LOGGER.info("Call to StructuresController.getOne with name = " + name);
Optional<Structure> found = this.structureRepository.findById(name);
if(found.isPresent()){
return ResponseEntity.ok(found.get());
}
else
{
return ResponseEntity.badRequest().body(null);
}
}
}
I've tried to use hibernate specific annotation #Type(type = "true_false"). It works but this produces an uppercase T or F in the database.
I've tried to write a JSON serializer that extends com.fasterxml.jackson.databind.ser.std.StdSerializer using this link without success.

Isn't your converter the wrong way round? Your convertToDatabaseColumn has it going from a String to a Boolean. Surely you want it going from Boolean to String. And the convertToEntityAttribute going from String to Boolean.
I suspect that the convert is returning a value of "t" or "f" which can't then be put into the boolean field in the entity.
I think it should be...
public class CharToBooleanConverter implements AttributeConverter<Boolean, String> {
#Override
public Boolean convertToEntityAttribute(String s) {
return s != null && s.equalsIgnoreCase("t");
}
#Override
public String convertToDatabaseColumn(Boolean aBoolean) {
return (aBoolean != null && aBoolean) ? "t" : "f";
}
}

Related

java spring boot Entity constructor with arguments does not execute

i do not understand why an Entity no argument constructor is being called when providing a request body? if i delete it and the only constructor that exist is the one that receive arguments, i get the expected output print, but i must implement a no argument constructor in order to save the Entity in the database.
this is the request body:
{
"str": "stringgg",
"intt": 2,
"doublee": 1.003
}
this is the route: when commenting out the empty constructor, the values of the new instance match the request json body
#PostMapping("/save")
public List<Modell> obj(#RequestBody Modell model) {
modelRepository.save(model);
System.out.println(model.toString());
return modelRepository.findAll();
}
this is the entity class:
#Table(name = "modelltbl")
#Entity
public class Modell {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", nullable = false)
private long id;
#Column(name = "str", nullable = true)
private String str;
#Column(name = "intt", nullable = true)
private int intt;
#Column(name = "doublee", nullable = true)
private double doublee;
public Modell(String str, int intt, double doublee)
{
this.str = str;
this.intt = intt;
this.doublee = doublee;
}
public Modell(){}
#Override
public String toString()
{
return String.format("model class,params: %s , %o , %f ", str , intt, doublee);
}
}
First of all: Do not use entities iat controller level. It is bad application Design.
The json will be converted throug jackson library which creates the object by calling the default constructor and the setter of the properties. If you do not want this behavior you can use the #JsonCreator annotation.
#JsonCreator
public Modell(#JsonProperty("str")String str, #JsonProperty("intt")int intt, #JsonProperty("doublee")double doublee)
{
this.str = str;
this.intt = intt;
this.doublee = doublee;
}

Java, list sorting with reflection

I want to allow to sort by every field in the class, without having to write switch/ if statements.
My idea was to find the Field that matches given string value by name and then, with Stream API neatly sort. IntelliJ screamed that i need to surround it with try-catch, so it is not so neatly looking, but that's not important, as it does not work.
private List<MyEntity> getSorted(List<MyEntity> list, SearchCriteria criteria) {
Field sortByField = findFieldInListByName(getFieldList(MyEntity.class), criteria.getSortBy());
return list.stream().sorted(Comparator.comparing(entity-> {
try {
return (MyEntity) sortByField.get(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return entity;
})).collect(Collectors.toList());
}
In the MyEntity class I have added Comparable interface, but I am not sure what should be in the body of Compare(), as I dont want to specify how to compare objects, because it will change based on the selected sorting.
EDIT: Added Entity below:
#Data
#Entity
#Table(name = "role_management", schema = "mdr")
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class MyEntity implements Comparable{
#Id
#Column(name = "uuid", unique = true, insertable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID uuid;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private UserEntity user;
#Basic
#NonNull
#Column(name = "role")
private String role;
#Basic
#Column(name = "action")
#Enumerated(EnumType.STRING)
private RoleAction action;
#Basic
#Column(name = "goal")
#Enumerated(EnumType.STRING)
private RoleGoal goal;
#Column(name = "date")
private LocalDateTime date;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reporter_id", referencedColumnName = "uuid")
private UserEntity reporter;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "authorizer_id", referencedColumnName = "uuid")
private UserEntity authorizer;
#Basic
#Column(name = "ezd")
private String ezd;
#Basic
#Column(name = "is_last")
private boolean isMostRecent;
#Override
public int compareTo(Object o) {
return 0;
}
}
EDIT 2: My code based on the #Sweeper solution:
UserEntity (nullable)
#Override
public int compareTo(UserEntity other) {
if (other == null) {
return 1;
}
return this.getMail().compareTo(other.getMail());
}
Comparator:
public static Comparator getSortComparator(Field sortByField) {
return Comparator.nullsLast(Comparator.comparing(entity -> {
try {
Object fieldValue = sortByField.get(entity);
if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
throw new IllegalArgumentException("...");
}
return (Comparable) fieldValue;
} catch (IllegalAccessException e) {
throw new MdrCommonException(e.getLocalizedMessage());
}
}));
}
MyEntity should not implement Comparable. It is the fields, by which you are going to sort the list of MyEntity objects, that needs to be Comparable. For example, if you are sorting by the field user, which is a UserEntity, then UserEntity is the thing that needs to be comparable, not MyEntity.
The lambda's job should just be to check that the fields are indeed Comparable, and throw an exception if they are not.
Since you don't know the types of the fields at compile time, however, you'd have to use a raw type here. The comparing call would look like this:
Comparator.comparing(entity -> {
try {
Object fieldValue = sortByField.get(entity);
// This check still passes if the type of fieldValue implements Comparable<U>,
// where U is an unrelated type from the type of fieldValue, but this is the
// best we can do here, since we don't know the type of field at compile time
if (!(fieldValue instanceof Comparable<?>) && fieldValue != null) {
throw new IllegalArgumentException("Field is not comparable!");
}
return (Comparable)fieldValue;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
You can create automatically comparators for any field of any class using reflection but is better create specific comparators (will be typechecked).
Your entity is a normal class with normal fields then, the usual Java sorting machinery should do the job:
Basically, if you define one comparator for every field (even deep fields into your entity):
public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
You can sort using complex sorting expressions:
data.stream()
.sorted(ByField2.reversed().thenComparing(ByDeep2))
.forEach(System.out::println);
a full example could be
public static void main(String[] args) {
List<MyEntity> data =
Stream.of("Row1", "Row2").flatMap(field1 ->
Stream.of(101, 102).flatMap(field2 ->
Stream.of(true, false).flatMap(field3 ->
Stream.of("Row1", "Row2").flatMap(deep1 ->
Stream.of(101, 102).flatMap(deep2 ->
Stream.of(true, false).map(deep3 ->
new MyEntity(field1, field2, field3, new MyDeepField(deep1, deep2, deep3))))))))
.collect(toList());
data.stream()
.sorted(ByField2.reversed().thenComparing(ByDeep2))
.forEach(System.out::println);
}
#Getter
#Setter
#AllArgsConstructor
static class MyDeepField {
private String deep1;
private Integer deep2;
private Boolean deep3;
}
#Getter
#Setter
#AllArgsConstructor
static class MyEntity {
private String field1;
private Integer field2;
private Boolean field3;
private MyDeepField field4;
public final static Comparator<MyEntity> ByField1 = comparing(MyEntity::getField1);
public final static Comparator<MyEntity> ByField2 = comparing(MyEntity::getField2);
public final static Comparator<MyEntity> ByField3 = comparing(MyEntity::getField3);
public final static Comparator<MyEntity> ByDeep1 = comparing(a -> a.getField4().getDeep1());
public final static Comparator<MyEntity> ByDeep2 = comparing(a -> a.getField4().getDeep2());
public final static Comparator<MyEntity> ByDeep3 = comparing(a -> a.getField4().getDeep3());
#Override
public String toString() {
return "MyEntity{" +
"field1='" + field1 + '\'' +
", field2=" + field2 +
", field3=" + field3 +
", deep1=" + field4.getDeep1() +
", deep2=" + field4.getDeep2() +
", deep3=" + field4.getDeep3() +
'}';
}
}
with output
MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=true}
MyEntity{field1='Row1', field2=102, field3=true, deep1=Row1, deep2=101, deep3=false}
...
MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=true}
MyEntity{field1='Row2', field2=101, field3=false, deep1=Row2, deep2=102, deep3=false}
The criteria field into your SearchCriteria class is some field of type Comparator<MyEntity> or a mapping using an enumeration or parsing string expressions or so...

Hibernate lazy attribute loading. False positive field detection with #DynamicUpdate

I have a Hibernate entity with a lazy attribute.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Entity
#Table(name = "rule", schema = "mariott_rule")
#DynamicUpdate // this is important
public class Rule {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "rule_id")
private Long id;
#Column(name = "switch")
private boolean switched;
#Column(name = "rule_name")
private String name;
private String description;
#Column(name = "date_update")
private ZonedDateTime dateUpdated;
#Column(name = "param_values", columnDefinition = "text")
#Convert(converter = RuleParamValuesAttributeConverter.class)
#Basic(fetch = LAZY)
private RuleParamValuesValidatedEntity paramValues;
}
And I have two methods that can either switch off or switch on the rule. Before switching the rule on we have be sure that paramValues are valid. That's why we fetch them with a separate query. But for the vice-versa operation it's not important. So, paramValues are not loaded in this case.
But here comes the strange part. The switching on operation generates the expected SQL.
validateParamValues(rule.getParamValues());
ruleRepository.saveAndFlush(
rule.asBuilder()
.switched(true)
.dateUpdated(now)
.build();
);
update mariott_rule.rule set date_update=?, switch=? where rule_id=?
Meantime the switching off operation adds paramValues to result SQL and assigns its value to "null" (the string value of null word).
// NO param values fetching
ruleRepository.saveAndFlush(
rule.asBuilder()
.switched(false)
.dateUpdated(now)
.build();
);
update mariott_rule.rule set date_update=?, param_values=?, switch=? where rule_id=?
binding parameter [1] as [TIMESTAMP] - [2021-04-09T08:10:28.423406Z]
binding parameter [2] as [VARCHAR] - [null]
binding parameter [3] as [BOOLEAN] - [false]
binding parameter [4] as [BIGINT] - [1]
Perhaps it's something about RuleParamsValuesAttributeConverter.class.
I removed the Basic(fetch = LAZY). Though it did help that's not what I want. Any ideas?
P.S. BTW, when I removed Basic(fetch = LAZY) it seems that #DynamicUpdate stopped to work either.
EDIT:
Here is the JsonAttributeConverter and RuleParamValuesAttributeConverter
#Slf4j
#RequiredArgsConstructor
public abstract class JsonAttributeConverter<A> implements AttributeConverter<A, String> {
private final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private final Class<A> entityClass;
private final String dbColumnName;
private final A defaultValue;
#Override
public String convertToDatabaseColumn(A attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (Exception e) {
throw new ConvertException(String.format("Cannot convert %s to string", entityClass.getSimpleName()), e);
}
}
#Override
public A convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, entityClass);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.warn(
String.format(
"Cannot convert database column %s to the %s. The default value shall be used",
dbColumnName,
entityClass.getSimpleName()),
e
);
}
return defaultValue;
}
}
}
#Converter
public class RuleParamValuesAttributeConverter extends JsonAttributeConverter<RuleParamValuesValidatedEntity> {
public RuleParamValuesAttributeConverter() {
super(RuleParamValuesValidatedEntity.class, "mariott_rule.rule.param_values", RuleParamValuesValidatedEntity.getInvalidInstance());
}
}

Simple xml returns null value for attribute field

I want to use Simple XML to deserialize the following XML into a POJO:
<shippingInfo>
<shippingServiceCost currencyId="USD">9.8</shippingServiceCost>
<shippingType>Flat</shippingType>
<shipToLocations>Worldwide</shipToLocations>
<expeditedShipping>true</expeditedShipping>
<oneDayShippingAvailable>false</oneDayShippingAvailable>
<handlingTime>3</handlingTime>
</shippingInfo>
I have created the following class to do so. However, I'm having trouble in that the currencyId attribute isn't being properly deserialized.
#Root(name = "shippingInfo")
public class ShippingInfo {
#Element(name = "shippingServiceCost", required = false)
private BigDecimal shippingServiceCost;
#Attribute(name = "currencyId", required = false)
private String currencyId;
#Element(name = "shippingType", required = false)
private String shippingType;
#Element(name = "shipToLocations" ,required = false)
private String shipToLocations;
#Element(name = "expeditedShipping", required = false)
private Boolean expeditedShipping;
#Element(name = "oneDayShippingAvailable", required = false)
private Boolean oneDayShippingAvailable;
#Element(name = "handlingTime", required = false)
private Integer handlingTime;
// Getters & Setters
public BigDecimal getShippingServiceCost() {
return shippingServiceCost;
}
public void setShippingServiceCost(BigDecimal shippingServiceCost) {
this.shippingServiceCost = shippingServiceCost;
}
public String getCurrencyId() {
return currencyId;
}
public void setCurrencyId(String currencyId) {
this.currencyId = currencyId;
}
public String getShippingType() {
return shippingType;
}
public void setShippingType(String shippingType) {
this.shippingType = shippingType;
}
public String getShipToLocations() {
return shipToLocations;
}
public void setShipToLocations(String shipToLocations) {
this.shipToLocations = shipToLocations;
}
public Boolean isExpeditedShipping() {
return expeditedShipping;
}
public void setExpeditedShipping(Boolean bool) {
this.expeditedShipping = bool;
}
public Boolean isOneDayShippingAvailable() {
return oneDayShippingAvailable;
}
public void setOneDayShippingAvailable(Boolean bool) {
this.oneDayShippingAvailable = bool;
}
public Integer getHandlingTime() {
return handlingTime;
}
public void setHandlingTime(Integer days) {
this.handlingTime = days;
}
}
I would expect the value of currencyId to be "USD" after deserializing, but I'm getting null. All the element values appear to deserialize properly. Does anyone have a suggestion on how to fix this?
Moreover, in a case such as the following instance:
<sellingStatus>
<currentPrice currencyId="USD">125.0</currentPrice>
<convertedCurrentPrice currencyId="USD">125.0</convertedCurrentPrice>
<bidCount>2</bidCount>
<sellingState>EndedWithSales</sellingState>
</sellingStatus>
Where there are two attributes named currencyId on two distinct elements, how can I go about deserializing these into separate fields? I have created a similar SellingStatus class but am unsure how to distinguish between the currencyId attributes.
Thank you!
Edit: Per suggestions I tried adding a custom ShippingServiceCost class to ShippingInfo as follows:
#Element(name = "shippingServiceCost", required = false)
private ShippingServiceCost shippingServiceCost;
Which in turn looks like this:
public class ShippingServiceCost {
#Element(name = "shippingServiceCost", required = false)
private BigDecimal shippingServiceCost;
#Attribute(name = "currencyId", required = false)
private String currencyId;
// getters and setters
}
But when I try to access both the shippingServiceCost field and the currencyId field, I get null in every instance (even though I know there is data). Any suggestions are greatly appreciated.
For the above code, SimpleXML expects the currencyId to be present as <shippingInfo currencyId="USD">.
So to solve it, you need to create another class called ShippingServiceCost which will contain the currencyId attribute and the BigDecimal
This will also solve your second query. You can do it by creating two classes CurrentPrice and ConvertedCurrentPrice which will contain the currencyId attribute.
The only working solution is creating a Converter class, see code below:
public class ShippingInfoConverter implements Converter<ShippingInfo> {
#Override
public ShippingInfo read(InputNode inputNode) throws Exception {
ShippingInfo shippingInfo = new ShippingInfo();
InputNode shippingServiceCostNode = inputNode.getNext("shippingServiceCost");
shippingInfo.setShippingServiceCost(new BigDecimal(shippingServiceCostNode.getValue()));
shippingInfo.setCurrencyId(shippingServiceCostNode.getAttribute("currencyId").getValue());
shippingInfo.setShippingType(inputNode.getNext("shippingType").getValue());
shippingInfo.setShipToLocations(inputNode.getNext("shipToLocations").getValue());
shippingInfo.setExpeditedShipping(Boolean.parseBoolean(inputNode.getNext("expeditedShipping").getValue()));
shippingInfo.setOneDayShippingAvailable(Boolean.parseBoolean(inputNode.getNext("oneDayShippingAvailable").getValue()));
shippingInfo.setHandlingTime(Integer.valueOf(inputNode.getNext("handlingTime").getValue()));
return shippingInfo;
}
#Override
public void write(OutputNode outputNode, ShippingInfo shippingInfo) throws Exception {
OutputNode shippingServiceCostNode = outputNode.getChild("shippingServiceCost");
shippingServiceCostNode.setValue(shippingInfo.getShippingServiceCost().toString());
shippingServiceCostNode.setAttribute("currencyId", shippingInfo.getCurrencyId());
outputNode.getChild("shippingType").setValue(shippingInfo.getShippingType());
outputNode.getChild("shipToLocations").setValue(shippingInfo.getShipToLocations());
outputNode.getChild("expeditedShipping").setValue(Boolean.toString(shippingInfo.isExpeditedShipping()));
outputNode.getChild("oneDayShippingAvailable").setValue(Boolean.toString(shippingInfo.isOneDayShippingAvailable()));
outputNode.getChild("handlingTime").setValue(Integer.toString(shippingInfo.getHandlingTime()));
}
}
Note how 'currencyId' is set, using the node's getAttribute method.
shippingInfo.setCurrencyId(shippingServiceCostNode.getAttribute("currencyId").getValue());
Also note how the element 'shippingServiceCost' gets the attribute
shippingServiceCostNode.setAttribute("currencyId", shippingInfo.getCurrencyId());
A few other things are need to get this working, starting with your POJO
#Root(name = "shippingInfo")
#Convert(ShippingInfoConverter.class)
public class ShippingInfo {
#Element(name = "shippingServiceCost", required = false)
private BigDecimal shippingServiceCost;
private String currencyId;
#Element(name = "shippingType", required = false)
private String shippingType;
#Element(name = "shipToLocations" ,required = false)
private String shipToLocations;
#Element(name = "expeditedShipping", required = false)
private Boolean expeditedShipping;
#Element(name = "oneDayShippingAvailable", required = false)
private Boolean oneDayShippingAvailable;
#Element(name = "handlingTime", required = false)
private Integer handlingTime;
// Getters & Setters
public BigDecimal getShippingServiceCost() {
return shippingServiceCost;
}
public void setShippingServiceCost(BigDecimal shippingServiceCost) {
this.shippingServiceCost = shippingServiceCost;
}
public String getCurrencyId() {
return currencyId;
}
public void setCurrencyId(String currencyId) {
this.currencyId = currencyId;
}
public String getShippingType() {
return shippingType;
}
public void setShippingType(String shippingType) {
this.shippingType = shippingType;
}
public String getShipToLocations() {
return shipToLocations;
}
public void setShipToLocations(String shipToLocations) {
this.shipToLocations = shipToLocations;
}
public Boolean isExpeditedShipping() {
return expeditedShipping;
}
public void setExpeditedShipping(Boolean bool) {
this.expeditedShipping = bool;
}
public Boolean isOneDayShippingAvailable() {
return oneDayShippingAvailable;
}
public void setOneDayShippingAvailable(Boolean bool) {
this.oneDayShippingAvailable = bool;
}
public Integer getHandlingTime() {
return handlingTime;
}
public void setHandlingTime(Integer days) {
this.handlingTime = days;
}
}
Adding the line below points SimpleXML to the converter class
#Convert(ShippingInfoConverter.class)
The other change is removing the #Attribute annotation.
One last thing required is that your driver class needs to have AnnotationStrategy enabled
when serialising and deserialing your objects.
Serializer serializer = new Persister(new AnnotationStrategy());

Getting JsonMappingException while sending data to view

I am trying to show DB data to my webpage.
I have made following code when GET request to the #RequestMapping(value = "/api/binder").
but when get request came to this method it will fetch data (I have print on console and display well) but it doesn't map to my Java Script Ajax call, it's showing me an error.
Following is my code for to fetch data :
#Autowired
IBinderViewRepository repository;
#RequestMapping(method= RequestMethod.GET)
public #ResponseBody
List<BinderResponse> getBinders(){
List<BinderView> binders = repository.getBinders();
List<BinderResponse> responses = new ArrayList<>();
ModelMapper mapper = Mapper.getInstance();
for(int i = 0; i < binders.size(); i++){
System.out.println("In Loop");
BinderResponse response = mapper.map(binders.get(i),BinderResponse.class);
System.out.println("Data :: " + response.getBinderName());
responses.add(response);
}
return responses;
}
but it shows me following error :
HTTP Status 500 - Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Here is ajax call from knockout js :
ajax.get('api/binder').done(function(response){ ... }
Here BinderView and BinderResponse have same fields :
private String binderName;
private String binderAddress1;
and getter setter as well in both.
and repository.genBinders() method bring data from DB.
Here is insert method and works fine for me :
#RequestMapping(method= RequestMethod.POST,consumes = "application/json")
public #ResponseBody
IWebApiResponse addBinder(#RequestBody AddBinderForm binder){
.....
}
Shall I have to put any json annotation on my BinderResponse class ?
I don't understand where am i wrong ?Anyone pleas guide me.
UPDATE :
public class BinderResponse extends WebApiResponseBase {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
BinderView :
public class BinderView extends BaseView {
private String binderName;
private String binderAddress1;
public String getBinderName() {
return binderName;
}
public void setBinderName(String binderName) {
this.binderName = binderName;
}
public String getBinderAddress1() {
return binderAddress1;
}
public void setBinderAddress1(String binderAddress1) {
this.binderAddress1 = binderAddress1;
}
}
In console it prints data / BinderName :
In Loop
Data :: ada
In Loop
Data :: tya
New Update :
Here is BaseView :
#MappedSuperclass
public abstract class BaseView implements IEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private long id;
public long getId() {
return id;
}
public void setId(long id) {
if (this.id != 0 && this.id != id) {
throw new IllegalStateException(
"The ID must not be changed after it is set.");
}
this.id = id;
}
}
and In IEntity :
public interface IEntity extends Serializable {
long getId();
void setId(long id);
}
WebApiResponseBase :
public class WebApiResponseBase implements IWebApiResponse {
private String _uri;
#Override
public String getUri() {
return _uri == null ? "" : _uri;
}
#Override
public void setUri(String uri) {
_uri = uri;
}
}
Jackson, by default, serializes an object's whole inheritance hierarchy, ie. the parent class fields as well. In the case of
public class BinderResponse extends WebApiResponseBase {
it seems like
Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.ngl.dto.outgoing.BinderResponse["valid"])
Jackson tries to serialize a field called valid from a getter called isValid (which is a conventional bean property name). The getter method, however, seems to throw a NullPointerException for whatever reason.
If you want Jackson to ignore it, you can annotate the getter with #JsonIgnore or your class with #JsonIgnoreProperties and specify the property name, ie. valid.
In my case when I used #JsonIgnore the exception has been gone but the problem was it couldn't receive that value from API Request anymore and Spring ignored it (obviously because of #JsonIgnore) So I investigated about the issue and figured out that the problem was the getter and setter.
I had the Integer property while my getter was int. So when I changed the getter to Integer my problem solved and error's gone.
private Integer purchaseId;
#JsonIgnore
public int getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(int purchaseId) {
this.purchaseId = purchaseId;
}
Changed to :
private Integer purchaseId;
public Integer getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(Integer purchaseId) {
this.purchaseId = purchaseId;
}
#Column(name="createddate")
private Date createdDate;
#Transient
private String formatedCreatedDate;
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
return dateFormat.format(this.getCreatedDate());
}
It throws the same exception because here may be null by calling getCreatedDate() value come so it can't format null date so keep null check here like:
Solution
public String getFormatedCreatedDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/mm/yyyy");
Date createDdate=this.getCreatedDate();
if(createDdate!=null){
return dateFormat.format(createDdate);
}
return "-";
}

Categories