YAML anchor label duplicates the key property of the anchored object - jackson

I use beans annotated with JAXB and the Jackson's JaxbAnnotationModule and YAMLFactory to export my objects to YAML.
public class Account {
#XmlID
protected String name;
public class Subtask
#XmlIDREF
protected Account account;
In the resulting YAML the account name is duplicated:
accounts:
- &JWFFTP name: "JWFFTP"
...
- &BREEZEFTP name: "BREEZEFTP"
...
...
Is it possible to tell Jackson to omit the property "name" from the YAML and when parsing it back, fill the value of "name" from the anchor label? Desired:
accounts:
- &JWFFTP ...
- &BREEZEFTP ...
...
Or the opposite, disable the use of YAML anchors and during the deserialization use the "name" property to lookup the value of the Subtask.account

Related

Adding a property using Jackson MixIn's?

I know we can use Jackson MixIn's to rename a property or to ignore a property (see examples here). But is it possible to add a property?
The added property can be:
A constant (such as a version number)
A computed value (e.g. if the source class has properties for getWidth() and getHeight(), but we want to ignore both and export a getArea() instead)
Flattened information from nested members (e.g. a class has a member Information which in turn has a member Description, and we want to have a new property for description and skipping the nesting structure of Information)
From documentation:
"Mix-in" annotations are a way to associate annotations with classes,
without modifying (target) classes themselves, originally intended to
help support 3rd party datatypes where user can not modify sources to
add annotations.
With mix-ins you can:
1. Define that annotations of a '''mix-in class''' (or interface)
2. will be used with a '''target class''' (or interface) such that it
appears
3. as if the ''target class'' had all annotations that the ''mix-in''
class has (for purposes of configuring serialization /
deserialization)
To solve your problems you can:
Create new POJO which has all required fields.
Implement custom serialiser.
Before serialisation convert POJO to Map and add/remove nodes.
Use com.fasterxml.jackson.databind.ser.BeanSerializerModifier to extend custom serialisers. See: Jackson custom serialization and deserialization.
For example, to add a constant version to each object you can wrap it in Verisoned class:
class Versioned {
private final String version;
#JsonUnwrapped
private final Object pojo;
public Versioned(String version, Object pojo) {
this.version = version;
this.pojo = pojo;
}
public String getVersion() {
return version;
}
public Object getPojo() {
return pojo;
}
}
Now, if you wrap an Arae(width, height) object:
Area area = new Area(11, 12);
String json = mapper.writeValueAsString(new Versioned("1.1", area));
output will be:
{
"version" : "1.1",
"width" : 11,
"height" : 12
}

Use Swagger/OpenAPI discriminator so that Jackson serializes object correctly

We're having trouble using the OpenAPI 2.0 discriminator in way that makes both the Swagger tools and the Jackson serializer happy.
Problem: during serialization Jackson currently generates two JSON properties for the discriminator, one of them having a null value.
OpenAPI 2.0 definition
swagger: '2.0'
info:
version: v1
title: Error API
paths:
/errors:
get:
description: Stack Overflow test
responses:
'200':
description: OK
schema:
$ref: '#/definitions/SpecificError'
definitions:
GeneralError:
description: Error Base Structure
type: object
discriminator: errorType
properties:
errorType:
type: string
message:
type: string
required:
- errorType
SpecificError:
description: Test
allOf:
- $ref: "#/definitions/GeneralError"
AFAIU the discriminator is correctly defined. The spec requires it to be listed both in the properties and the required list.
The property name used MUST be defined at this schema and it MUST be in the required property list. When used, the value MUST be the name of this schema or any schema that inherits it.
Swagger codegen
What the Swagger Java codegen produces is this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "errorType",
visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = SpecificError.class, name = "SpecificError"),
})
public class GeneralError {
#JsonProperty("errorType")
private String errorType = null;
// accessors, even for errorType!, follow here
The accessors for errorType come as a big surprise. As the field is only needed during serialization & deserialization regular client code shouldn't have access to it. One could even argue that the field shouldn't be there at all.
Jackson serializer
As a simple test bed I use this
SpecificError specificError = (SpecificError) new SpecificError().message("message")
ObjectMapper objectMapper = new ObjectMapper();
ObjectWriter writer = objectMapper.writer();
writer.writeValue(System.out, specificError);
This produces {"errorType":"SpecificError","message":"message","errorType":null}.
-> errorType appears twice
Q: whose fault is it? Is my Swagger definition wrong? Should the Swagger Java codegen not generate private String errorType? Or should Jackson be able to deal with this i.e. recognize that its #JsonTypeInfo and the property of that name are actually the same thing?

Jackson deserialization: How to get a default value even if the JSON property was null

In my project I'm using Jersey 2.23.1 with Jackson for JSON support.
When I'm getting a request with something like { "foo":null, "bar":"123" } as JSON, matching with class A{String foo; String bar;} Jersey first creates and instance of A (with default values if specified in constructor), then deserialize JSON to a temporary object A', then copies all JSON fields that were specified in JSON from A' to A. If I have default values in A-class constructor, and have fields equal to null in JSON, all my default values are erased and replaced by null. So in the example above, if I have a default value for the foo field, it will be replaced by null in the object Jersey will return as param for my #Path annotated method.
I'm using #JsonInclude(Include.NON_NULL) on A class to avoid the transfer of null fields during Response. But it only works for serialization, what about deserialization? I mean, when having { "foo":null } as JSON results in field "foo" = null in new object instance after deserialization.
Here is some code to sum all of this :
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(value = Include.NON_NULL)
public class User {
public enum EUserRole {
PARENT, STUDENT, PROF, ADMIN
}
#Id
public String id;
public String firstName;
public String lastName;
public EUserRole role;
public User() {
id = ObjectId.get().toString();
role = EUserRole.STUDENT;
lastName = "RandomLastName";
}
}
if I'm passing this kind of JSON
{
"id":null,
"lastName":null,
"firstName":"Random First Name",
"role":"STUDENT"
}
to my method (in controller)
#POST
public Response createUser(final User entity) {
}
it results that all null fields in JSON are set to null in my entity and not set to the constructor default values.
Do you know if there is a way to specify Jackson to ignore null fields during deserialization? Or is this a Jersey-related behavior?
There is no way to ignore data from JSON payload in that sense, based on value contained (you can use ignoral to just ignore all values for given property).
So if you want to avoid null assignment, you need define a setter that will just swallow null value (that is, only assign non-null).
Ability to prevent null assignment might a useful feature to add via #JsonFormat.Feature, something like:
// hypothetical no such feature exists yes
#JsonFormat(without = JsonFormat.Feature.ALLOW_NULL_ASSIGNMENT)
so perhaps this could be a feature request.
And the reason I think this belongs to per-property handling is that this seems like a specific rule for some of the properties. Although perhaps there could also be a matching global setting if it seems users really like such null-ignoral.

how to hide field during serialization (but not deserialization)

In our project (springMVC) Rest API project I wish to only use ONE model for both request and response (to avoid having to add tons of code to copy field from object to object)
I'd like to use Swagger to handle all the doc, but I'm running into a little problem. For example let say I have a model User
public class User {
private Long id;
private String username;
private String password;
}
And a simple controller
public void createUser(#RequestBody User user)...
public User getUser(Long id) ..
Now I would like swagger to hide the property password on deserialization but not serialization (so having it display for the Input but the output)
and the opposite for the Id field.
I have tried using #JsonIgnore coupled with #JsonProperty but on the swagager-ui it either displays everything or hides everything. I cannot manage to it work.
Could someone indicate me what is the best way of archiving my goal ? Is it possible to use a single model for request and response while using swagger? In case it is not possible to use #JsonIgnore, is there a way to archive this differently ?
Swagger doesn't want you to have different input/output models with the same name. You should simply create an interface and attach that to the input, and for the output extend that interface or add an implementation with the additional field. For example, please see here for modeling tips:
https://swaggerhub.com/api/swagger-tutorials/modeling-samples/1.0.0
Your exact use case is one of them. The solution posted in the above link is here:
definitions:
User:
description: this is a user that would be passed into the system
properties:
username:
type: string
UserResponse:
allOf:
- $ref: '#/definitions/User'
- type: object
required:
- id
properties:
id:
type: string
format: uuid
readOnly: true
where User is the input object, and UserResponse is the output object, with the additional id field.
Add #JsonIgnore with getter of the field and #JsonProperty with the setter or with the field . As Due to use of immutable code or final fields sometime setter doesn't work.
example :
public class Student {
private Float name;
private String rollnum;
private String section;
#JsonProperty
private Boolean passOrFailed;
#JsonIgnore
public Boolean getpassOrFailed {
return active;
}
}
Remember to use both else else it will lead to removing element in deserialization

jdto superclass boolean field binding incorrect value

public class Model {
}
public class SuperclassDTO {
private boolean funny = true;
public boolean isFunny() {
return funny;
}
public boolean setFunny(boolean f) {
this.funny = f;
}
}
public class SubclassDTO extends SuperclassDTO {
}
new SubclassDTO().isFunny() //returns true
SubclassDTO dto = binder.bindFromBusinessObject(SubclassDTO.class, new Model());
dto.isFunny(); //returns false!!!!
Isn't this weird? Model class does not have a "funny" field but somehow dto is bind with a wrong value. First I thought jDTO required "getFunny" convention, so it couldn't read the value and just set it "false" but changing the getter name to "getFunny" does not resolve the issue, plus I'm not allowed to modify SuperclassDTO. How can I bind the correct value?
Jdto version 1.4 by the way...
The behavior you're experiencing is a "side effect" of the convention over configuration approach. All the fields on the DTO are configured unless you mark them as transient, either by using the #DTOTransient annotation or the transient configuration on the XML file. If a configured field does not have a corresponding field on the source bean, it will be set with default values and that is the reason why you're experiencing this behavior.
You have some options to overcome this issue:
Add the #DTOTransient annotation to the DTO.
Since you're not able to modify the DTO, you could configure it through XML.
Use Binding lifecycle to Restore the value. By adding code on the subclass.
You might as well submit a bug report on the jDTO issue tracker on github.