Convert object that contains another object to json - java

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;
}

Related

Spring Java POST endpoint that can have a field be different data types

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
}
}

How to cancel #jsonIgnore in specific method

I have a domain javabean, some bean hvae a lot of information with password
and login Ip, I use the #jsonIgnore to filter that property which I dont
want the end user know.
But there has a problem,In other method
I use the same javabean to send back
to front side,but now I need some property from this domain
has anyway can cancel this #jsonIgnore in some specific method?
#JsonIgnore
private String address;
private Integer drawnum;
but now I need address , I cant do this.....
I dont want to use the for loop to add in other object.
I think that what you are looking for is the concept of JsonView : in some cases you want a set of attributes to be serialized, and in some other cases you want a (slightly) different set of attributes to be serialized.
Check this excellent tutorial, it explains evrything, even the use with Spring MVC.
Create classes to annotate the fields :
public class Views {
public static class Public {
}
public static class Internal extends Public {
}
}
Annotate the fields :
public class Item {
#JsonView(Views.Public.class)
public int id;
#JsonView(Views.Public.class)
public int drawnum;
#JsonView(Views.Internal.class)
public String address;
}
In the controller, if you want only "public" properties to be serialized ;
#JsonView(Views.Public.class)
#RequestMapping("/items/{id}")
public Item publicItem(#PathVariable int id) {
Result : {"id":2,"drawnum":5}
In another controller, if you want all properties to be serialized ;
#JsonView(Views.Internal.class)
#RequestMapping("/items/{id}")
public Item internalItem(#PathVariable int id) {
Result : {"id":2,"drawnum":5,"address":"My address"}

How to move several params to another node in jackson?

I have to generate strange json from object(with jackson ObjectMapper), something like:
{
"data":{
"id":"1",
"name":"Json"
},
"userType":"MD"
}
But, with jackson data-bind i can do only:
{
"id":"1",
"name":"Json",
"userType":"MD"
}
With User.class:
public class User {
private String id;
private String name;
private String userType;
//Getters Setters Constructor
I found two ways of bypassing. First one - to use another "superclass":
public class Data {
#JsonProperty("data")
private User user;
private String userType;
//Getters Setters Constructor
Second way - to use Map:
Map<String, Object> map = new HashMap<>();
map.put("data", user);
map.put("userType", "MD");
String json = objMapper.writeValueAsString(map);
But i think, that it's not idea to use this ways if i have about 8 different objects with such structure. So, what's best pratice? Or maybe there are another built in ways?
I want to find the best one
The first approach you mentioned is the best practice, but it is called Composition (HAS-A relationship) rather 'superclass' and just to add readability, you can refactor your classes like this:
public class User {
private Data data;
private String userType;
//Getters Setters Constructor
}
public class Data {
private String id;
private String name;
// getters and setters
}
So, when you marshal your pojo to json, structure will be:
{
"data":{
"id":"1",
"name":"Json"
},
"userType":"MD"
}
which is what you want! Happy coding :)
Update: If you are generating your API documentation with Swagger or Open API Spec, then there is no way to have a representation of your model if you use Map or HashMap. So, obviously, the first approach is the one you should consider!

How to hide fields with a condition in RESTful WS?

I have a class called Report that I need to share using RESTful WS.
once in full with all its attributes
once in only a reduced version
Normally I'd use something like #XmlTransient to hide the fields, but this would prevent the full version from working.
Is there any way to set a condition or to kind of pre-filter fields just before the output so that it doesn't affect other uses of the same class?
My Report class looks like this:
public class Report {
private String reportId;
private String title;
private String content;
private Date created;
private Date modified;
...
}
The RESTful sharing for the full Report looks like this:
#GET
#Path("/{reportId}")
public Report getReport(#PathParam("reportId") String reportId) {
return Mock.getReport(reportId);
}
The full output I need looks like this:
{
"reportId": "d83badf3",
"title": "The tales of lamaru",
"content": "When once the great Imgur started his journey...",
"created": 1519672434866,
"modified": 1519672434866
}
The short output I need should look like this:
{
"reportId": "d83badf3",
"title": "The tales of lamaru"
}
What is necessary to achieve this?
Why don't you use Inheritance?
Parent
public class Report {
private String reportId;
private String title;
}
Child
public class FullReport extends Report{
private String content;
private Date createdp;
private Date modified;
}
When you need full report set return type as FullReport otherwise Report
Jackson has two different annotations to use when you want to exclude some class members from the JSON serialization and deserialization processes. These two annotations are #JsonIgnore and #JsonIgnoreProperties.
#JsonIgnoreProperties is an annotation at the class level and it expects that the properties to be excluded would be explicitly indicated in the form of a list of strings.
#JsonIgnore instead is a member-level or method-level annotation, which expects that the properties to be excluded are marked one by one.
try this.
public class Report {
private String reportId;
private String title;
#JsonIgnore
private String content;
#JsonIgnore
private Date created;
#JsonIgnore
private Date modified;
...
}

Using Spring #RequestBody to convert JSON with mixed convention in payload to Entity Object

I am using Spring #RequestBody to map a JSON payload to a Java Object. Unfortunately this JSON payload does not use a set convention but rather has names that use both camelCase and snake_case.
To be clear my Controller looks like this:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Object> flagDevice (#RequestBody List<MobileDeviceData> deviceInfoList) {
... code here ...
}
with the MobileDeviceData Entity object having several setter methods like:
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setFlagId(int flagId) {
this.flagId = flagId;
}
This works great and without any extra effort when the JSON objects name is camelCase. However for snake_case names I need to add the Annotation:
#JsonProperty("flag_id")
private int flagId;
in order for it to be picked up.
I know it's not a good idea to use the #JsonProperty if it can be avoided as you then will need to annotate every parameter. My question is, is there a more general way to enforce matching snake_case with the corresponding camelCase in the Entity object? And obviously to do it without screwing up the ones that are already camelCase.
As per the article here, there is a simple approach to deserialize the MobileDeviceData class. Here is the sample code as below:
#JsonDeserialize(using = UserDeserializer.class)
public class User {
private ObjectId id;
private String username;
private String password;
public User(ObjectId id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public ObjectId getId() { return id; }
public String getUsername() { return username; }
public String getPassword() { return password; }
}
Assume User is the class we’re interested in writing the Deserializer for. Not much is notable here, except for the annotations that tell Jackson who knows how deserialize this class.
public class UserDeserializer extends JsonDeserializer {
#Override
public User deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
return new User(null,
node.get("username").getTextValue(),
node.get("password").getTextValue());
}
}
The deserializer is created by extending Jackson’s abstract JsonDeserializer class, and giving it the type we want to deserialize to. Difficult is figuring out that you can reference the JSON by field name with the JsonParser's ObjectCodec.
I hope it helps.
Please feel free to comment if needed!
Having been working on this a bit, I now realize doing anything like what was requested would be counterproductive.
When you receive (deserialize) a JSON Object, it is generally expected that you will deliver (serialize) with the same parameters. If an implementation extracted both camelCase and underscore parameters the same way, then it would not know how to deserialize correctly later on. By following a standard convention and then using #JsonProperty for all the exceptions, it remains possible to deserialize and later deliver the JSON object just as it was received.

Categories