I am trying to setup a spring-boot configuration that uses different properties which have the same base class. The problem is that the base type is always null.
I am doing this because I want have different configuration elements that have a set of required properties and also have some additional properties .
For context: The configuration file looks as follows and is saved as JSON:
{
// some other properties
"layout": {
"test": 1,
"element": {
"#type": "traffic",
"id": "id-1",
"district": "district"
}
}
For this layout property I use the following class:
#ConfigurationProperties(prefix = "layout")
#ToString
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class LayoutProperties {
/* This is always correct */
private int test;
/* This is always null! */
private LayoutObject element;
}
The base class looks a follows:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY
)
#JsonSubTypes({
#JsonSubTypes.Type(value = TrafficLayoutObject.class, name = "traffic"),
})
public abstract class LayoutObject {
public abstract String getId();
}
where I use the #JsonTypeInfo and the #JsonSubTypes annotations as suggested in some tutorials.
An implementation of this class is this one:
#ConstructorBinding
#ToString
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class TrafficLayoutObject extends LayoutObject {
private String id;
private String district;
}
which essentially adds an additional district field.
The problem now is that whenever I startup my application the elements field if declared as LayoutObject is null. However, when using the specific type (e.g. TrafficLayoutObject) the field is filled properly by spring.
Another interesting thing is that Jackson seems to handle this correctly because when deserializing the layout object from the config, everything is filled correctly.
What am I missing? Or is there a better way to do this?
Related
How do you put defaults values from application.properties to an Entity?
This works but is not what I need.
#Entity
#Data
public class SomeEntity {
private String value = "default";
}
I know that this code doesn't work because I've used new(), so Spring doesn't inject the value. Adding #Component didn't change.
#Entity
#Data
public class SomeEntity {
#Value("${value}")
private String value;
}
//other place
SomeEntity se = new SomeEntity()
System.out.println(se.value); //null
how can i add ${value} to something like this?
(I like this way because database knows the default value too)
#Entity
#Data
public class SomeEntity {
#Column(columnDefinition = "varchar(255) default [INJECT_HERE]");
private String value;
}
You cannot inject properties in an Entity via Spring because you create it yourself. If you really want to have a default value driven by a property, I would do a factory with the injected value that creates those SomeEntity and set the given property with the right field.
I have really simple requirement to exclude Boolean type attribute from payload during Jackson serialization. Following is the piece of code that I want to fix that. I want exclude it always irrespective to its value.
#Getter
#Setter
#NoArgsConstructor
#XmlRootElement
public class Order{
#JsonIgnore
private boolean userPresent;
}
Can someone help me on this?
You should explicitly add the Getter for the property you want to ignore and set the #JsonIgnore there:
#Getter
#Setter
#NoArgsConstructor
#XmlRootElement
public class Order{
private boolean userPresent;
#JsonIgnore
public boolean isUserPresent() {
return this.userPresent;
}
}
If you don't have any other properties in the class, you should remove the #Getter annotation because it is redundant now.
I am working on an e-policy project where i need to save different types of policies. For simplicity i am considering only two types "LifeInsurance" and "AutoInsurance". What i want to achieve is if the JSON request to create policy contains "type":"AUTO_INSURANCE" then the request should be mapped to AutoInsurance.class likewise for LifeInsurance but currently in spring boot app the request is getting mapped to parent class Policy eliminating the specific request fields for auto/Life insurance. The domain model i have created is as below.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include =
JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({ #Type(value = AutoInsurance.class, name = "AUTO_INSURANCE"),
#Type(value = LifeInsurance.class) })
public class Policy {
#Id
#GeneratedValue
private Long id;
private String policyNumber;
#Enumerated(EnumType.STRING)
private PolicyType policyType;
private String name;
}
My AutoInsurance class is below.
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonTypeName(value = "AUTO_INSURANCE")
public class AutoInsurance extends Policy {
#Id
#GeneratedValue
private Long id;
private String vehicleNumber;
private String model;
private String vehicleType;
private String vehicleName;
}
Below is LifeInsurance type child class
#Entity
#NoArgsConstructor
#Getter
#Setter
#JsonTypeName(value = "LIFE_INSURANCE")
public class LifeInsurance extends Policy {
#OneToMany(mappedBy = "policy")
private List<Dependents> dependents;
private String medicalIssues;
private String medication;
private String treatments;
}
To save the policy details, I am sending JSON request from UI with a "type" property indicating the type of insurance in the request.
When i run the below test method, JSON request gets mapped to the correct child class as required.
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper map = new ObjectMapper();
String s = "{\"id\": 1,\"policyNumber\": \"Aut-123\",\"type\": \"AUTO_INSURANCE\",\"policyType\": \"AUTO_INSURANCE\",\"name\": null,\"address\": null,\"contact\": null,\"agentNumber\": null,\"agentName\": null,\"issuedOn\": null,\"startDate\": null,\"endDate\": null,\"vehicleNumber\": \"HR\",\"model\": null,\"vehicleType\": \"SUV\",\"vehicleName\": null}";
Policy p = map.readValue(s, Policy.class);
System.out.println(p.getClass());
//SpringApplication.run(EPolicyApplication.class, args);
}
But when i run the same in Spring boot in a RESTController postmapping, I am getting a PArent class object instead of the child class object.
#RestController
#RequestMapping("/policy")
public class PolicyController {
#PostMapping
public void savePolicy(Policy policy) {
System.out.println(policy.getClass());
}
}
I can get the JSON as string, autowire objectmapper and parse manually but i want to understand if its a known issue and if anyone else has faced the same with Spring boot. I have searched for solutions on this but i got was solution to deserializing to polymorphic classes but nothing related to issue with Spring boot.
In your method you haven't annotated the Policy method argument with #RequestBody. Which leads to Spring creating just an instance of Policy instead of using Jackson to convert the request body.
#PostMapping
public void savePolicy(#RequestBody Policy policy) {
System.out.println(policy.getClass());
}
Adding the #RequestBody will make that Spring uses Jackson to deserialize the request body and with that your annotations/configuration will be effective.
I'm trying to create a Document with an #Id property different from String, everything works great. My issue is the way the document is stored in the Bucket, unfortunately it prefixes with the Class name:
ActorKey(key=d7471027-4bd1-40a3-8e29-5249f45beed4)
{
"name": "Emma Watson",
"_class": "com.guilherme.miguel.domain.Actor"
}
Is there a way to store the id like if it was a simple String?
This is to avoid issues in plain queries (simple queries will force me to use ActorKey(key=xxxxxxxxxxx))
Actor.java:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document
public class Actor {
#Id
private ActorKey key;
private String name;
}
ActorKey.java:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ActorKey implements Serializable {
private String key;
}
ActorRepository.java
#N1qlPrimaryIndexed
#ViewIndexed(designDoc = "actor")
public interface ActorRepository extends CouchbaseRepository<Actor, ActorKey> {
}
The underlying Couchbase SDK uses String as a Document identifier, so spring-data-couchbase will call toString() on your key objects. What you see is the result of the Lombok-generated toString() method (a generation that is part of the #Data annotation).
Simply explicitly override toString() in ActorKey!
I need to take XML that looks like something like the following:
<root:ElementName>
<equipment:Equipment>
<eqp:Name>Equipment 1</eqp:Name>
<eqp:Type>A</eqp:Type>
</equipment:Equipment>
<equipment:Equipment>
<eqp:Name>Equipment 2</eqp:Name>
<eqp:Type>B</eqp:Type>
</equipment:Equipment><equipment:Equipment>
<eqp:Name>Equipment 3</eqp:Name>
<eqp:Type>C</eqp:Type>
</equipment:Equipment>
</root:ElementName>
And I want to map that into a list of "Equipment" POJOs. I'm using Jackson XML mapping and Lombok, so basically I've got this split into two classes right now, first the root object which should read in that <root:ElementName> and turn all the <equipment:Equipment> tags into a list of equipment objects:
#JacksonXmlRootElement(localName = "root:ElementName")
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class EquipmentMidbCompositeResponse
{
#JsonProperty("equipment")
#JacksonXmlProperty(localName = "equipment:Equipment")
#Getter
#Setter
List<Equipment> equipmentList;
}
And then the Equipment object itself:
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Equipment
{
#JsonCreator
public Equipment(String name){
}
#JsonProperty("EquipmentName")
#JacksonXmlProperty(localName = "eqp:Name")
#Setter
#Getter
private String name;
#JsonProperty("EquipmentType")
#JacksonXmlProperty(localName = "eqp:Type")
#Setter
#Getter
private String type;
}
At first I didn't have that constructor with #JsonCreator in the Equipment object and would get a "no String-argument constructor/factory method to deserialize from String value" error, and after some research added the constructor to get around that. With that I get past that error, but the list of Equipment objects that gets returned after mapping have all their fields set to null. What am I missing/doing wrong here when trying to map these XML properties?
(Posted on behalf of the OP).
I figured out the issue, my approach with the #JsonCreator was the incorrect way to go for that error. Turns out all I had to do was an a #JacksonXmlElementWrapper(useWrapping=false) annotation to my list item and everything went through just fine.