How to create xml webservice DTOs depending on conditions? - java

Is it possible to create a global DTO for an xml webservice, but having conditional fields inside?
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MyDTO {
...
#XmlElementWrapper
#XmlElement(name = "somename")
private List<String> list;
}
Now, what if I want to release another version of the webservice, and rename the #XmlElement field thereby (or introduce additional fields, remove some, etc).
So that backwards compatibility is retained, but the same objects are used for the "new" version.
I could maybe do this my adding request path methods with /v1, /v2 etc.
But how could I then maintain a single DTO class, but with fields conditional on the version path?
Or would I always have to duplicate those DTO classes and modify exactly to me needs of the version?
#RestController
public void MyServlet {
#RequestMapping("/v1")
public MyDTO1 request1() {
}
#RequestMapping("/v2")
public MyDTO2 request2() {
}
}

I would prefer using tailored DTOs for each version of the API. To avoid boilerplate code when mapping your entities to DTOs, you could consider using mapping frameworks such as MapStruct.
If you are using Jackson, you could consider using JSON Views (they will work with XML too). Quoting from the Latest Jackson integration improvements in Spring article:
JSON Views
It can sometimes be useful to filter contextually objects serialized to the HTTP response body. In order to provide such capabilities, Spring MVC now has builtin support for Jackson’s Serialization Views (as of Spring Framework 4.2, JSON Views are supported on #MessageMapping handler methods as well).
The following example illustrates how to use #JsonView to filter fields depending on the context of serialization - e.g. getting a “summary” view when dealing with collections, and getting a full representation when dealing with a single resource:
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
Thanks to Spring MVC #JsonView support, it is possible to choose, on a per handler method basis, which field should be serialized:
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}
In this example, if all messages are retrieved, only the most important fields are serialized thanks to the getAllMessages() method annotated with #JsonView(View.Summary.class):
[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
In Spring MVC default configuration, MapperFeature.DEFAULT_VIEW_INCLUSION is set to false. That means that when enabling a JSON View, non annotated fields or properties like body or recipients are not serialized.
When a specific Message is retrieved using the getMessage() handler method (no JSON View specified), all fields are serialized as expected:
{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "bclozel#pivotal.io",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "snicoll#pivotal.io",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "rstoyanchev#pivotal.io",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
Only one class or interface can be specified with the #JsonView annotation, but you can use inheritance to represent JSON View hierarchies (if a field is part of a JSON View, it will be also part of parent view). For example, this handler method will serialize fields annotated with #JsonView(View.Summary.class) and #JsonView(View.SummaryWithRecipients.class):
public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
#JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.SummaryWithRecipients.class)
#RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}

Related

Aggregation Java MongoDB with MongoTemplate not return group value

In my Spring Controller I cannot figure out how to retrieve correctly the result of a MongoDB with MongoTemplate aggregation. Below is the code from my Controller class:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("modality").is("check")),
Aggregation.group("name").count().as("check_count"),
Aggregation.project("name").and("check_count").as("check_count")
);
AggregationResults<Response> res = mongoTemplate.aggregate(aggregation, "user", Response.class);
Below the simple class of User and Response:
#Document(collection = "user")
class User{
#Id
private String id;
private String name;
private String modality;
//constructor and get/set
}
class Response{
private String name;
private string check_count;
//constructor and get/set
}
So I retrieve correctly my response but I do not see the name, that is always null:
{
"user": [
{
"name": null,
"check_count": 61
},
{
"name": null,
"check_count": 15
},...
What is wrong in my Aggregation.group ? Thanks
When you do the group aggregation, the pipeline that gets generated is mostly
{ "$group" : { "_id" : "$name", "check_count" : { "$sum" : 1}}}
So, the result of the group stage will have _id as the field, not name.
Subsequent, stage should use _id field.
Aggregation.project().and("_id").as("name").and("check_count").as("check_count");

Deserialization of Object serialized by #JsonIdentityInfo

I am having my User class annotated like this to remove cyclic format of output:
#Entity
#Table(name = "USER")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = User.class)
public class User extends AbstractValueObject {
private Integer id;
private String name;
.....
}
public class Load extends AbstractValueObject {
private Integer id;
private User postedBy;
}
So whenever i fetch List of Load it is giving me output as below JSON :
[
{
"id" : 1,
"postedBy" : {
"id":1,
"name":"SOF"
}
},
{
"id" : 2,
"postedBy" : 1
}
]
But client side wants it in original format - say each object of load should contain full postedBy object. Client side is in Android - Java.
Is there any way at Android end to de-serialize object in original format at Android ?
Expected output :
[
{
"id" : 1,
"postedBy" : {
"id":1,
"name":"SOF"
}
},
{
"id" : 2,
"postedBy" : {
"id":1,
"name":"SOF"
}
}
]
I tried with JSOG but in some cases it fails.
Any help will be appreciated. :)
You can use Jsog Converter, which provide library to encode and decode objects.
https://github.com/jsog/jsog

java jpa json standard

First, thank you very much for reading this question.
I have a JPA project and everything works fine, the json that i get with the controller is of this form:
{"id": 1, "name": "Canada"},{"id": 2, "name": "USA"}
All its fine but i would like to get a json with the Jsend standard, it something like this:
{
status : "success",
data : {
"country" : [
{"id": 1, "name": "Canada"},
{"id": 2, "name": "USA"}
]
}
}
{
"status" : "fail",
"data" : { "title" : "A title is required" }
}
{
"status" : "error",
"message" : "Unable to communicate with database"
}
As you can see i want to have a status that says success, fail or error:
But i dont know how to do it. This is my DTO, DAO and Controller
#Entity
public class Country implements Serializable {
private static final long serialVersionUID = -7256468460105939L;
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name;
//Constructor, get and set
DAO
#Repository
#Transactional
public class CountryRepository {
#PersistenceContext
EntityManager entityManager;
public CountryDTO findById(int id) {
return entityManager.find(CountryDTO.class, id);
}
}
Controller
#RestController
public class CountryController {
#Autowired
CountryDTO repository;
#RequestMapping(value="api/country/{id}", method=RequestMethod.GET)
public #ResponseBody CountryDTO getByID(#PathVariable("id") int id){
return repository.findById(id);
}
}
Again thank you for your time.
Its very good question from my point of view. So I can give list of action items to achieve this.
You should aware of #ControllerAdvice annotation which is available in Spring.
By utilizing that you can play with your response object.
Then You should create your own Object which is similar to JSend. In my case, I have created JSendMessage class
public class JSendMessage {
String status;
String message;
Object data;
// Add getter and setter
}
Now you should map above class with your #ControllerAdvice return your required object.
So whenever there is a exception you can create and send your own custom exception message.
There will be lot of reference for this. Just look for #ControllerAdvice

How to map one-to-one relationship with JPA in spring boot?

I have read several tutorial about one-to-one relation mapping forexample:
https://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html,
http://websystique.com/hibernate/hibernate-one-to-one-unidirectional-with-foreign-key-associations-annotation-example/
https://en.wikibooks.org/wiki/Java_Persistence/OneToOne
I beleive I follow these tutorials, however my relational mapping still not works as expected. I have the following classes:
#Entity(name = "lesson")
public class Lesson {
#Id
#Type(type = "pg-uuid")
private UUID uid;
private String start_date_time;
private String end_date_time;
private String location;
#OneToOne
#JoinColumn(name="uid") //uid is the name of the Id i want to reference to in the subject class
private Subject subject_uid; // subject_uid is the name of the column in my subject table
public Lesson(UUID uid, String start_date_time, String end_date_time, String location, Subject subject_uid) {
this.uid = uid;
this.start_date_time = start_date_time;
this.end_date_time = end_date_time;
this.location = location;
this.subject_uid = subject_uid;
}
//getters setters
#Entity(name = "subject")
public class Subject {
#Id
#Type(type = "pg-uuid")
private UUID uid;
private String name;
private String start_date;
private String end_date;
private boolean is_lesson_created;
public Subject(UUID uid, String name, String start_date, String end_date, boolean is_lesson_created) {
this.uid = uid;
this.name = name;
this.start_date = start_date;
this.end_date = end_date;
this.is_lesson_created = is_lesson_created;
}
The response what the Spring Data Rest creates on /lessons endpoint looks the following:
{
"_embedded" : {
"lessons" : [ {
"start_date_time" : "2017-01-08 08:30:00",
"end_date_time" : "2017-01-08 10:15:00",
"location" : "A101 ",
"_links" : {
"self" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500"
},
"lesson" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500"
},
"subject_uid" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500/subject_uid"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:3400/lessons{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://localhost:3400/profile/lessons"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
When I want access the http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500/subject_uidlink I get a 404.
Is the UUID type effects my mapping? What should I change to be able to access my student_uid?
Finally I found out the problem, which is something I haven't read anywhere. When a one-to-one join has to be done, JPA provides the default name of the join as the table name underscore id(subject_id). In my case, I have a tablename called "subject" in the database and the PK called simply "uid". So what you have to do is append the table name with the name of the attribute, which to join to:
#OneToOne
#JoinColumn(name="subject_uid")//the pattern is: "tablename_joined attribute"
private Subject subject_uid;

Serialize List content in a flat structure in jackson json (Java)

The class I need to Serialize:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class MyClass {
#JsonProperty("CustomerId")
private String customerId;
#JsonProperty("Products")
private List<ProductDetails> products;
//Getters and setters
}
My ProductDetails.java class:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class ProductDetails {
#JsonProperty("ProductId")
private String productId;
#JsonProperty("ProductName")
private String productName;
//Getters and setters
}
The default serialized output:
{
"CustomerId" : "ewm0po",
"Products" : [ {
"ProductId" : "AAA",
"ProductName" : "AAA Product"
}, {
"ProductId" : "AAA",
"ProductName" : "AAA Product"
}]
}
The output I'm trying to get:
{
"CustomerId" : "ewm0po",
"ProductId1" : "AAA",
"ProductName1" : "AAA Product"
"ProductId2" : "AAA",
"ProductName2" : "AAA Product"
}
In other words, I am trying to skip the JSON brackets for the Products-list and suffix each of the ProductId and ProductName fields with a increasing integer.
Any help is very much appreciated!
As #Boris the spider pointed out. Writing a custom serializer was the solution. Not as painful as I expected :-)

Categories