How to exclude Weld metadata from JSON object serialization - jax-rs

Assume the following REST resource:
#Path("/ActiveLeadTask")
//Also possible MediaType.APPLICATION_XML
#Produces(MediaType.APPLICATION_JSON)
public class ActiveLeadTask {
private #Inject ActiveLeadTaskBo activeLeadBo;
#GET
#Path("/getBo")
public ActiveLeadTaskBo getBo() {
return activeLeadBo;
}
}
////////////////////////////////////////////////////
#XmlRootElement
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class ActiveLeadTaskBo implements Serializable {
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
private String phoneNumber;
private String phoneCountryCode;
private AtomicInteger accessCounterField = new AtomicInteger(0);
public ActiveLeadTaskBo() {
firstName = "test";
lastName = "test";
}
public int getAccessCounter() {
return accessCounterField.incrementAndGet();
}
public void setAccessCounter(int seed) {
accessCounterField.set(seed);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
The REST response for getBo() JSON (but not for XML) returns the following:
{
"accessCounter": 1,
"firstName": "test",
"lastName": "test",
"metadata": {
"contextualInstance": {
"accessCounter": 2,
"firstName": "test",
"lastName": "test"
},
"instance": {
"accessCounter": 3,
"firstName": "test",
"lastName": "test"
}
}
}
The JSON response contains an additional "metadata" field - how can I configure the project to not generate it, or avoid generating it? The CDI container is Weld and the JSON serializer is provided by Yasson.

Two solutions are possible:
create a wrapper class, for example, ActiveLeadTaskBoInjectWrapper:
#Inject
ActiveLeadTaskBoInjectWrapper activeLeadBo;
activeLeadBo.getInstance();
workaround Weld specifics:
#Inject
Foo foo;
public void doSomething() {
if (foo instanceof WeldClientProxy) {
// foo is a proxy
((WeldClientProxy)foo).getMetadata().getContextualInstance()
} else {
// not a proxy
}
}

Depends on what JSON processing framework is used in your REST endpoint. For jsonb-api (jsr367) the only possible solution is to implement javax.json.bind.config.PropertyVisibilityStrategy to exclude generated properties from processing.

Related

How to return ObjectNode from Controller in Micronaut?

I have following code
#Controller()
public class TestController {
#Get(value = "test", produces = MediaType.APPLICATION_JSON)
public MyDto fetch() throws Exception {
return new MyDto(
"test",
new ObjectMapper().readValue("{\"a\": 1}", ObjectNode.class)
);
}
#Serializable
#Data
public static class MyDto {
private final String name;
private final ObjectNode extraFields;
public MyDto(String name, ObjectNode extraFields) {
this.name = name;
this.extraFields = extraFields;
}
}
}
And I have an unexpected output on the client, extraFields object is empty
{
"name": "test",
"extraFields": [
[]
]
}
How to make Micronaut controller properly serialize com.fasterxml.jackson.databind.node.ObjectNode ?

Join the result of several microservice in a Mono

I try to develop a api with spring-cloud-gateway but it's not easy for me
My "microservice A" return an Mono. This Object contains a List of id "Object B"
My "microservice B" return an Flux
In my api-gateway, how can i aggregate in a Mono the result of the microservices ?
#Service
public class ServiceAClient {
private final WebClient webClient;
public ServiceAClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl("lb://microservice-A/A/").build();
}
public Mono<ObjectA> getObjectA(String id){
return webClient
.get()
.uri("{id}" , id)
.retrieve()
.bodyToMono(ObjectA.class)
.onErrorResume(ex->Mono.empty());
}
}
#Service
public class ServiceBClient {
private final WebClient webClient;
public ServiceAClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl("lb://microservice-B/B/").build();
}
public Flux<ObjectB> getListObjectB(List<Long> ids){
return webClient
.get()
.uri("{ids}" , ids)
.retrieve()
.bodyToFlux(ObjectB.class);
}
}
#Data
public class ObjectA {
private UUID id;
private String name;
private String description;
private Date start;
private Date end;
private List<Long> listIdObjectB;
}
#Data
public class ObjectB {
private Long id;
private String name;
private String localisation;
}
#Data
public class MyDto {
private UUID id;
private String name;
private String description;
private Date start;
private Date end;
private List<ObjectB> listObjectB;
}
#Service
#AllArgsConstructor
public class CombinedService {
private final ServiceAClient serviceAClient;
private final ServiceBClient serviceBClient;
public Mono<MyDto> getDetails(String id){
// return MyDto who join a Mono Service A an Flux service B
}
}
The desired result
{
"id": "2355e7eb-edf7-428c-b51b-ac06c146ed3c",
"name": "toto",
"description": "lorem ipsum",
"debut": 01/06/2022,
"fin": 10/06/2022,
"ListObjectB": [
{
"id": 1,
"name": "foo",
"localisation": "here"
},
{
"id": 2,
"name": "bar",
"localisation": "here"
}
]
}
Thank you in advance for your help
You can take it as a start point to understand a possible solution.
I highly recommend you to read about reactive streams(https://www.reactive-streams.org/) and spring-webflux(https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html).
Your CombinedService should looks like:
#Service
#AllArgsConstructor
public class CombinedService {
private final ServiceAClient serviceAClient;
private final ServiceBClient serviceBClient;
public Mono<MyDto> getDetails(String id) {
return serviceAClient.getObjectA(id)
.map(objectA -> {
final Flux<ObjectB> fluxB = serviceBClient.getListObjectB(objectA.getListIdObjectB());
final List<ObjectB> listObjectB = fluxB.toStream().collect(Collectors.toList());
final MyDto myDto = new MyDto();
myDto.setName(objectA.getDescription());
myDto.setDescription(objectA.getDescription());
myDto.setListObjectB(listObjectB);
//More setters, etc
return myDto;
});
}
}

Getting "no serializer found for class" Exception in restAssured post request

I have a Json Payload for a Post call as below:
{
"action" : "Closed",
"Id" : 30144,
"expireDate" : null,
"inputUser" : "abc",
"previousStatusId" : 1,
"statusId" : 4,
"Notes" : [ ]
}
My POJO classes for the above payload is as below
public class UpdateNoteStatus {
private String action;
private int Id;
private String expireDate;
private String inputUser;
private int previousStatusId;
private int statusId;
private List<Notes> Notes;
public void setAction(String action) {
this.action = action;
}
public void setId(int Id) {
this.Id = Id;
}
public void setExpireDate(String expireDate) {
this.expireDate = expireDate;
}
public void setinputUser(String inputUser) {
this.inputUser = inputUser;
}
public void setPreviousStatusId(int previousStatusId) {
this.previousStatusId = previousStatusId;
}
public void setStatusId(int statusId) {
this.statusId = statusId;
}
public void setNotes(List<Notes> Notes) {
this.Notes = Notes;
}
}
public class Notes{
}
Now I have assigned the values in the main class from where I am making the API call is as below:
ArrayList<Notes> Notes = new ArrayList<Notes>();
UpdateNoteStatus objUpdateNoteStatus = new UpdateNoteStatus();
objUpdateNoteStatus.setAction("Closed");
objUpdateNoteStatus.setId(Integer.parseInt("30144"));
objUpdateNoteStatus.setinputUser("abc");
objUpdateNoteStatus.setPreviousStatusId(1);
objUpdateNoteStatus.setStatusId(4);
objUpdateNoteStatus.setNotes(Notes);
But when I am making the API POST call it is throwing exception - "no serializer found for class and no properties discovered to create beanserializer". Could you please help. The Step is hightlighted in Bold.
RequestSpecification rs = given().contentType("application/json");
**rs = rs.body(objUpdateNoteStatus);** //In This Step I am getting the above mentioned Exception
Response res = rs.when().post("/UpdateStatus");
as you are initializing an empty object , you need to use below Annotation supported in below library
com.jayway.restassured.RestAssured;
#JsonIgnoreProperties(ignoreUnknown=true)
class UpdateNoteStatus

How to use the TempData attribute to pass data to a Razor view

I have:
namespace Test
{
public interface ITest
{
public string Test1(string s);
}
public class Test : ITest
{
[Microsoft.AspNetCore.Mvc.TempData]
public string Message
{
get; set;
}
public string Test1(string s)
{
Message = "Test " + s;
return "Test has run";
}
}
}
And in Startup.cs:
services.AddScoped<Test.ITest, Test.Test>();
Then in a Razor view:
#inject Test.ITest Test
<p>Result is #Test.Test1("Hello World!")</p>
<p>TempData["Message"] is #TempData["Message"]</p>
The output is:
Result is Test has run
TempData["Message"] is
Where have I done incorrectly? How can I pass a TempData["Message"] from some code (that is not in a Controller) to a Razor page?
How can I pass a TempData["Message"] from some code (that is not in a Controller) to a Razor page?
If you'd like to retain and pass value via TempData inside your custom service, you can try the following code snippet.
public interface ITest
{
public string Test1(string s);
public string Test2();
}
public class Test : ITest
{
private readonly IHttpContextAccessor _httpContextAccessor;
public Test(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string Message
{
get {
var tempDataDictionaryFactory = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
var tempDataDictionary = tempDataDictionaryFactory.GetTempData(_httpContextAccessor.HttpContext);
if (tempDataDictionary.TryGetValue("Message", out object value))
{
return (string)value;
};
return "";
}
set
{
var tempDataDictionaryFactory = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
var tempDataDictionary = tempDataDictionaryFactory.GetTempData(_httpContextAccessor.HttpContext);
tempDataDictionary.Remove("Message");
tempDataDictionary.TryAdd("Message", value);
}
}
public string Test1(string s)
{
Message = "Test " + s;
return "Test has run";
}
public string Test2()
{
return Message;
}
}
In Razor Page
<p>Result is #Test.Test1("Hello World!")</p>
<p>TempData["Message"] is #TempData.Peek("Message")</p>
<p>#Test.Test2()</p>
Test Result

Swagger Request custom class as Number

I have extended the BigDecimal class as a Premium class, because I want to add some default behavior (i.e. the MathContext). In the created swagger.json a BigDecimal is presented as:
"schema": {
"type": "number"
}
while the Premium class is presented as:
"schema": {
"$ref": "#/definitions/Premium"
}
and:
"definitions": {
"Premium": {
"type": "object"
}
}
How can I annotate the Premium class so that it is represented in the swagger.json as a "type": "number"?
The Premium class looks as follows:
public class Premium extends BigDecimal {
private static final long serialVersionUID = 1L;
private static final MathContext DEFAULT_MC = MathContext.DECIMAL64;
public Premium(String val) {
super(val);
}
#JsonCreator
public Premium(#JsonProperty BigDecimal bd) {
this(bd.toString());
}
public Premium multiply(Premium val) {
return new Premium(super.multiply(val));
}
public Premium divide(Premium val) {
return new Premium(super.divide(val, DEFAULT_MC));
}
public Premium subtract(Premium val) {
return new Premium(super.subtract(val));
}
}
And I created a REST service that looks as follows:
#Path("/test")
#Api
public class PremiumSvc {
#POST
#Path("/pr")
#ApiOperation("Premium.")
public Premium doPr(Premium req) {
return req.subtract(new Premium("0.02"));
}
#POST
#Path("/bd")
#ApiOperation("BigDecimal.")
public BigDecimal doBd(BigDecimal req) {
return req.subtract(new BigDecimal("0.02"));
}
}