In Spring 3.3 I have an entity which is mapped to a database table. In this entity class I have all properies annotated with #JsonProperty, for instance #JsonProperty("ID").
Stepping into the controller a service is called to get such an entity by using a DAO/repository. This works well but when I send this entity back to the requestor using #ResponseBody all properties are sent twice. Once as demanded but one more time beginning lowercase until the first camel case letter occurs.
An example...
public class MyEntity {
#JsonProperty("MYSpecialSuperId")
private String MYSpecialSuperId;
...
public String getMYSpecialSsuperId() {
return this.MYSpecialSuperId;
}
}
After JSON stringifying the result is:
{ "MYSpecialSuperId":""9", "myspecialSuperId":"9" }
Why is the property twice in the result and why is the lettering different???
BTW:
It was not my idea to let Java properties begin with an uppercase letter even yet with more than one uppercase letter.
Jackson's ObjectMapper uses the Java bean pattern. In other words, it expects the following
public class Foo {
public Object bar;
public Object getBar() {...}
public void setBar(Object bar) {...}
}
The getters and setters start with get and set, respectively, followed by the corresponding field name with its first letter capitalized. If you change your code to
public class MyEntity {
#JsonProperty("MYSpecialSuperId")
private String mySpecialSuperId;
...
public String getMySpecialSuperId() {
return this.mySpecialSuperId;
}
}
Note that the field starts with lowercase my instead of uppercase (regardless of the #JsonProperty value), I removed the extra s in getMYSpecialSsuperId and used a lowercase y. So now the field name matches the getter name and jackson knows that the property is the same and doesn't need to serialize twice.
If you have no choice, you can follow what Katona posted in the comments and use
#JsonAutoDetect(getterVisibility=Visibility.NONE)
to make jackson ignore the getters completely and only use the fields to serialize your JSON.
I think you have a typo in your accessor; if it has "SsuperId" it does not match name of the field; and as such field and getter are taken to mean different logical properties.
Related
Spring will automatically bind properties defined in application.properties or application.yml to fields defined in #ConfigurationProperties-annotated classes. For instance, in my application.properties I can have:
fizz.buzz=35
fizz.foo=hello
And in my Java code I can have:
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer buzz;
private String foo;
// ...
}
And at runtime FizzProperties#buzz will get a value of 35 injected into it and FizzProperties#foo will have a value of "hello" injected into it.
I'm wondering what the naming convention is for camel-cased Java fields, and also for hyphens ("-") and periods (".") used in the properties files. For instance, if I had:
fizz.whistle-feather=true
fizz.baz.boo=always
What would their corresponding Java fields need to look like in order for Spring to map and inject them properly?
public class Baz {
private String boo;
}
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer whistleFeather; // correct?
private Baz baz; // correct?
// ...
}
Are my assumptions correct here or misled (and if misled, how so)? I can't find this explained in the Spring docs.
As stated in spring-boot docs, it uses "relaxed binding", so both properties "whistle-feather" and "whistleFeather" will be mapped to your private Integer whistleFeather, but it's recommended, when possible, to store properties in lower-case kebab format, such as fizz.whistle-feather=10.
So your first case is correct.
The second case is also correct, because dots are used as delimiters in application.properties, while YAML file uses as delimiters both dots and colon.
You also may define nested properties as nested classes to store them in one place like this:
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer whistleFeather;
private Baz baz;
// getters, setters
public static class Baz {
private String boo;
// getters, setters
}
}
Take a look here for more info about spring-boot properties binding and examples.
Take this example:
class Car {
Door leftDoor;
Door rightDoor;
Door getLeftDoor();
Door getRightDoor();
}
class Door {
String getType();
}
The goal is to have this json:
{
"Door.lefttype": "A",
"Door.righttype": "B"
}
I have the object mapper configured that only things with #JsonProperty are converted to Json.
If I only had one door, I could simply put #JsonProperty("door.type") on String getDoorType(). But since there are multiple instances of the same type I can't put the annotation on the last class. Furthermore, I need #JsonUnwrapped because I don't want it to make a hierarchy in the json. I want this:
door.lefttype: "A"
instead of
door: {
lefttype: "A"
}
What I have so far (I'm using interfaces + mixins since I don't have access to the classes directly):
public interface CarMixin {
#JsonProperty
#JsonUnwrapped
Door getLeftDoor();
#JsonProperty
#JsonUnwrapped
Door getRightDoor();
}
public interface DoorMixIn {
#JsonProperty
String getType();
}
I need the exact names, so this doesn't suffice. I need to use named JsonProperties
I don't think I have the same understanding of mixin as you do, and I must say I'm little unclear what is the extend of your limitation in modifying existing classes, but I would think something along the following lines might help:
class Car {
Door leftDoor ;
Door rightDoor ;
#JsonProperty("Door.lefttype")
String leftType() {
return leftDoor.getType() ;
}
#JsonProperty("Door.righttype")
String leftType() {
return rightDoor.getType() ;
}
}
I have a class Order:
#Data
#Entity
public class Order {
private List<Project> projects;
// more fields
}
I have a two API methods in my controller:
#GetMapping
public ResponseEntity<List<Order>> getOrders() {
return ResponseEntity.ok(orderService.getOrders());
}
#GetMapping("/{id}")
public ResponseEntity<Order> getOrder(#PathVariable long id) {
return ResponseEntity.ok(orderService.getOrder(id));
}
So in this case projects is always sent via JSON, if its present its just getting serialized, if its not present its getting fetched lazily and then serialized. I could avoid it being serialized by annotating the field with #JsonIgnore. But the problem is that i want to send it sometimes and sometimes i dont. For example in getOrders() i dont want the projects to be serialized. In getOrder(...) i would want projects to be serialized. Is there any way to tell during runtime either inside custom code or by an annotation that i want to send it in one specific case and not in another case? The only thing i figured out is that - shortly before serializing - i can initialize projects with null and annotate the entity with #JsonInclude(JsonInclude.Include.NON_NULL). That way it wouldnt be sent and if i want to send it i can just avoid initializing it with null. But obviously i dont want to iterate over each Order in O(n) just to initialize its projects with null.
This is easy to achieve using "JSON Views".
First, define some classes to represent each view (e.g. internal/external):
public class OrderViews {
public static class OnlySomeFields {}
public static class AllFields extends OnlySomeFields {}
}
Next, on your class, assign a view to each field:
public class Order {
#JsonView(OrderViews.OnlySomeFields.class)
private String foo;
#JsonView(OrderViews.AllFields.class)
private String bar;
// getters/setters/etc
}
Then, in your controller, you can specify which view to use for each method:
#RestController
public class MyController {
#JsonView(OrderViews.AllFields.class)
#GetMapping("/with-all-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
#JsonView(OrderViews.OnlySomeFields.class)
#GetMapping("/with-some-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
}
With this setup, navigating to /with-all-fields returns a JSON containing foo and bar, while navigating to /with-some-fields returns a JSON only containing foo.
You can use this technique to selectively serialize specific fields, and should be able to apply it to your use case.
Here's my situation,
I have a class with Enum type fields. I want to execute annotated validation for enum types, similar to annotations for strings, example: #Size, #NotNull etc.
Problem is, json deserializer fails on enum type before validation occurs.
public class myClass {
#JsonProperty
//#SomeCustomValidator -- ??
private EnumType enumValue;
}
public enum EnumType {
A,
B,
C
}
Few things:
I do not want to change the data type to String.
Tried steps in following threads, didn't fix my problem.
Tried this link, but get an error in deserialization before validation hits
Tried this link, but it works only when data Type is String
Validation works after the type of the argument is resolved. So I don't see a way how to use String validating annotations on enums. As workaround you can use #JsonCreator and do some validation before object creation.
public enum EnumType {
A,
B,
C;
#JsonCreator
public static EnumType from(String s) {
// add your logic here, for example
/*
if (Stream.of(values()).map(Enum::name).noneMatch(name -> name.equals(s))) {
throw new MyServiceException("Bad value " + s);
}
*/
return valueOf(s);
}
}
I have a simple controller method with more than 7 parameters and would like to refactor it using a model object instead, i.e. to extract a parameter object:
#RequestMapping(value="/{one}")
public String controllerMethod(#PathVariable(value="one") String one, #RequestParam String two, ... #RequestBody body) {
...
}
I tried to extract an object with setters and getters and pathVariable and requestParameters are mapped by name. However I have troubles making the same for #RequestBody, it doesn't work for me even if I put #RequestBody into setter...
public class Parameters {
private String one; // pathVariable
private String two; // requestParameter
private String body;// requestBody - always NULL!
// other fields definition
public setBody(#RequestBody String body) {this.body = body}
//other setters/getters
}
How to keep #RequestBody parameter in extracted POJO?
Another question is how to control name of parameters, i.e. if
parameter name differs from the field name in POJO, is there any
annotation? This one doesn't work:
public void setOne(#RequestParameter(value="o") String one) {this.one = one}
How to mark the fields as required or give a default value like in the #RequestParameter annotation?
For the (1) I would simply keep #RequestBody as a separate parameter though I don't like it much.
Ok, looks like the only way of doing (2) and (3) is through customizing data binding: the similar question
Feel free to post another easy solution if you know about it.