I have a class X:
class X {
#NotNull
#NotEmpty
private final String name;
#JsonValue
public String name() {
return name;
}
#JsonCreator
public X(final String name) {
this.name = name;
}
}
In another class, I have the following:
class Input {
#Valid
#JsonProperty("data")
private List<List<X>> data;
}
I am parsing a JSON input file with Jersey and validate with Hibernate Validator. The validation does not seem to be executed - an empty string is accepted.
"data": [
[
""
],
[
"name",
"location"
]
]
You can write your own annotiation to check two dimensional list on top of the project:
https://github.com/jirutka/validator-collection
It shows how to validate elements in a collection. It could be helpful in your case because neither jsr303 nor jsr349 gives you possibilty to validate out of the box
This usecase is not supported by Bean Validation. The #Valid annotation on List<List<X>> data; will instruct the validator to iterate each List<X> element and check for its validity. For this the class List will be checked for bean validation constraints, which don't exist (your constraint is on X). #Valid is not recursive in any form.
A workaround would be to use your own wrapper class to host the list of X instances. In this wrapper class you then can annotate the wrapped list with #Valid.
Related
I am new to Spring Boot and I am trying to figure out how to parse json data. I see a lot of tutorials on how to map json string object to an annotated Java class and using and object mapper, like this:
json:
{
"UUID": "xyz",
"name": "some name"
}
public class MyClass{
#JsonProperty
private UUID id;
#JsonProperty
private String name;
#JsonAnyGetter
public UUID getId() {
return this.id;
}
#JsonAnySetter
public void setId(UUID id) {
this.id = id;
}
#JsonAnyGetter
public String getName() {
return this.name;
}
#JsonAnySetter
public void setName(String name) {
this.name = name;
}
}
ObjectMapper objectMapper = new ObjectMapper();
MyClass customer = objectMapper.readValue(jsonString, MyClass.class);
The problem is that the system I am getting the json string from does not match the class naming conventions we use (and I cannot change either one). So, instead of having the example json string above, it might look like this:
{
"randomdstring-fieldId": "xyz",
"anotherrandomstring-name": "some name"
}
This use case only has two fields, but my use case has a larger payload. Is there a way to either map the field names from the json object to the field names in the Java class or is there a way to just parse the json string as a key value pair (so that I can just manually add the fields to my Java object)?
In Jackson with #JsonProperty you can customize the field name with it's annotation parameter value
Therefore, you just have to annotate the entity fields with the #JsonProperty annotation and provide a custom JSON property name, like this:
public class MyClass{
#JsonProperty("original_field_name_in_json")
private UUID id;
...
The #JsonProperty will do it for you:
#JsonProperty("name_in_json")
private Long value;
I'm working on a Java Spring Boot HTTP Service application. I currently have a POST endpoint that I have defined inside of a #RestController. This controller, called processRequest takes an object called Info with the #RequestBody annotation.
Right now, I have it setup where a user can send JSON based on the Info class that I defined like this:
//Sample JSON Payload
{
"name": "Bob",
"age": 26,
"hobby": biking
}
//Sample Object
#RequiredArgsConstructor
public class Info {
public final String name;
public final int age;
public final String hobby
}
What I want to do know is respond to the situation where one of the fields is sent as a different datatype. For example:
//JSON payload with different datatype for a field
{
"name": "Bob",
age: 26,
"hobby": ["biking", "hiking"] //This is supposed to be a string but it's an array.
}
Is it possible to keep the endpoint properties the same but handle different data types? Maybe I can create another class where the fields are different and spring will automatically create the one that matches the input? I'm curious for what the best approach to this problem would be.
In this particular example, where the hobby could either be a single value or multiple values, I would rely on the Jackson ACCEPT_SINGLE_VALUE_AS_ARRAY deserialization feature.
This can be configured application-wide within application.properties:
spring.jackson.deserialization.accept-single-value-as-array=true
Or this can be enabled for a specific field:
#RequiredArgsConstructor
public class Info {
public final String name;
public final int age;
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public final List<String> hobby
}
For more complex cases, Jackson recommends that you write a wrapper with a specific type field to provide a hint of which type it should deserialize. For example:
public class InfoWrapper {
private String type;
#JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = PersonInfo.class, name = "person")
})
private Info info;
}
public interface Info {}
#RequiredArgsConstructor
public class PersonInfo implements Info {
public final String name;
public final int age;
public final String hobby
}
So, if you want to send a JSON containing PersonInfo, you can use the following JSON:
{
"type": "person",
"info": {
"name": "Bob",
"age": 26,
"hobby": "biking"
}
}
If you need more advanced control over what you want to do, you can write a custom deserializer and apply it with the #JsonDeserialize annotation.
You can use JsonNode for the field which changes. As below:
public class Info {
public String name;
public int age;
public JsonNode hobby;
#Schema(description = "")
#Valid
#JsonIgnore
public List<String> getHobbies() {
// if hobby is array create Java object and return
// else if hobby is String create List, add the string to it and return it.
// or some other solution of your choice and requirement
}
}
Let's say I have following flat JSON structure:
{
"name": "name",
"validFrom": "2018-01-09",
"validTo": "2018-01-10",
}
and MyPojo class:
public class MyPojo {
private String name;
#JsonUnwrapped
private Validity validity;
}
and Validity class:
public class Validity {
private LocalDate validFrom;
private LocalDate validTo;
}
I created custom unwrapping serializer and it works fine.
I would like to deserialize JSON above into MyPojo class which includes Validity value object.
How should custom deserializer for Validity be implemented?
#JsonProperty does not work as I want to use 2 Json properties for Validity construction
I would recommend a constructor in this case, a lot simpler than a custom deserializer, something like:
#JsonCreator
public MyPojo(#JsonProperty("name") String name,
#JsonProperty("validFrom") String validFrom,
#JsonProperty("validTo") String validTo) {
this.name = name;
this.validity = new Validity(validFrom, validTo);
}
It's implied that LocalDate is parsed from String above but you may have Jackson parse them.
You may skip annotations above if you use Java 8 with parameter names module
That will require an extra annotation on validity, see open Jackson issue here
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#JsonUnwrapped
private Validity validity;
I'm using spring rest and jackson to generate json. For the class country
public class Country extends AbstractPersistable<Long> {
private String name;
private String code2;
private String code3;
public Country() {
}
public Country(String name, String code2, String code3) {
...
}
...
}
I get, for example,
{
"id" : 1,
"name" : "Afghanistan",
"code2" : "AF",
"code3" : "AFG",
**"new" : false**
}
For some classes I get an unexpected "new" field always set to false. I suspect it has something to do with the parametrized constructor, but it's just a guess. Ideas?
The class AbstractPersistable has a public method called isNew specified by the interface Persistable (the doc here).
You will have to ignore such property if you don't want it in your JSON, for example, using the annotation JsonIgnoreProperties in your class.
I need to pass data from HTML to Service using JSON (in JS/JQUERY)
In the service I have a service call that reciving an object that contains another object and 2 more fields.
Role object:
import java.io.Serializable;
public class Role implements Serializable {
private long id;
private String name;
}
User object:
import java.io.Serializable;
public class User implements Serializable {
private String userName;
private String password;
private ArrayList<Role> roles;
}
Till now I managed to pass data to JSON like that to other service: (data that contains only 2 parameters: id and userName )
xmlHttp.open("POST", "http://www.foo.com/serviceFunction2", true);
xmlHttp.send('{"Id": "123", "userName": "test"}');
So, my question is how can I fill the User object that contains the Role object using JS/JQUERY? like I managed to send it with this line:
xmlHttp.send('{"Id": "123", "userName": "test"}');
Thanks
There are several ways to "nest" objects in JS, but for your purpose probably the simplest is to nest your object and/or array literals. If "roles" is supposed to be an array, then something like this:
'{"userName":"test", "password":"secret", "roles":["role1","role2","role3"]}'
If each role in the array is itself an object with properties:
{
"userName":"test",
"password":"secret",
"roles": [
{ "roleName":"role1", "prop2":"someValue", "prop3":"whatever" },
{ "roleName":"role2", "prop2":"someValue", "prop3":"whatever" },
{ "roleName":"role3", "prop2":"someValue", "prop3":"whatever" }
]
}
(Line breaks added for readability, but you'd remove them if creating a string.)
I don't know what your goal is but if you are using the HTML page as a GUI and Java for the processing you might as well use jsf or similar. There are multiple frameworks like primefaces, myfaces or icefaces that handle a lot of stuff for you.
Have a look at the Primefaces showcase if you're interested.
As MattBall suggested you can also use something like jaxb, this takes a lot of load off your hands.
You can use a lot of powerful and easy stuff.
You can map objects to JSON/XML using annotations:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Role
{
#XmlAttribute
private long id;
#XmlAttribute
private String name;
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class User
{
#XmlAttribute
private String userName;
#XmlAttribute
private String password;
#XmlElement
private ArrayList<Role> roles;
}
You can then use this in a web service:
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("user/{username}")
public User getUser( #PathParam("username") String userName)
{
User user;
// Get your user
return user;
}