How to return ObjectNode from Controller in Micronaut? - serialization

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 ?

Related

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 send Collection of abstract types with ActionResult

I am trying to send back a collection of abstract types in a Controller using ActionResult.I do not know how to tell the serializer to also include derived type(s) specific properties:
public abstract class Base
{
public int Id{get;set;}
}
public class D1:Base
{
public string D1Value{get;set;}
}
public class D2:Base
{
public bool IsD2Value{get;set;}
}
public async Task<ActionResult<IEnumerable<Base>>> GetAll()
{
var collection=new []{ new D1 { Id=1, D1Value="hi"} ,new D2 {Id=2, IsD2Value=true}};
return StatusCode(200,collection);
}
How can i reach this result in a easy and elegant way.I have checked the JsonSerializer options but in my case i am not the one that is doing the serialization.
What i get
[{ "Id":1} , { "Id":2 }]
What i want
[{ "Id":1,"D1Value":"hi" } , { "Id":2 , "IsD2Value":true }]
Try the following code:
public async Task<ActionResult<IEnumerable<Base>>> GetAll()
{
var collection = new List<object>()
{
new D1 { Id = 1, D1Value = "hi" },
new D2 { Id = 2, IsD2Value = true }
};
return StatusCode(200, collection);
}
Here is the test result:
Use new ArrayList() instead of List.

Enum Keys are discarded on Deserialization

I am trying to deserialize the following json structure to object.
"policyDetail": {
"policies": {
"API_KEY": {
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"username": "username",
"password": "password",
"policyEnabled": true
}
}
}
In this structure, API_KEY and BASIC_AUTH are java Enum type. I am trying to deserialize like this.
In Service class
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(Policy.class, new PolicyDeserializer());
mapper.registerModule(module);
PolicyDeserializer.java
public class PolicyDeserializer extends StdDeserializer{
public PolicyDeserializer() {
super(Policy.class);
}
protected PolicyDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Policy deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = jp.readValueAsTree();
// Here I am only getting values under API_KEY not the entire API_KEY and underneath structure.
JsonNode customField = node.findValue("API_KEY");
Policy result = null;
if(customField != null && !customField.isNull()) {
ApiKeyPolicy apiKeyPolicy = new ApiKeyPolicy();
apiKeyPolicy = mapper.readValue(customField.toString(), ApiKeyPolicy.class);
result = apiKeyPolicy;
return result;
}
return result;
}
}
Policy.java
package model.policy;
import enums.PolicyType;
public abstract class Policy {
private PolicyType policyType;
private boolean isPolicyEnabled;
public Policy(PolicyType policyType) {
this.policyType = policyType;
}
/**
* #return the isPolicyEnabled
*/
public boolean isPolicyEnabled() {
return isPolicyEnabled;
}
/**
* #param isPolicyEnabled
* the isPolicyEnabled to set
*/
public void setPolicyEnabled(boolean isPolicyEnabled) {
this.isPolicyEnabled = isPolicyEnabled;
}
}
ApiKeyPolicy.java
import enums.PolicyType;
import model.Policy;
public class ApiKeyPolicy extends Policy {
private String isEnabled;
public ApiKeyPolicy() {
super(PolicyType.API_KEY);
}
/**
* #return the isEnabled
*/
public String getIsEnabled() {
return isEnabled;
}
/**
* #param isEnabled the isEnabled to set
*/
public void setIsEnabled(String isEnabled) {
this.isEnabled = isEnabled;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "ApiKeyPolicy [isEnabled=" + isEnabled + "]";
}
}
PolicyDetail.java
import java.util.EnumMap;
import java.util.Map;
import enums.PolicyType;
public class PolicyDetail {
EnumMap<PolicyType, Policy> policyMap = null;
public PolicyDetail() {
if(policyMap == null) {
policyMap = new EnumMap<PolicyType,Policy>(PolicyType.class);
}
}
public void addPolicy(PolicyType policyType, Policy policy, boolean isEnabled) {
if(null != policy) {
policy.setPolicyEnabled(isEnabled);
this.policyMap.put(policyType, policy);
}
}
public Map<PolicyType, Policy> getPolicies(){
return this.policyMap;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "PolicyDetail [policyMap=" + policyMap + "]";
}
}
As this is how 3rd party Json structure. Hence, I can not change the existing structure. Can you please suggest where am I missing?
Thanks in advance.. !
After too much of research and hit-and-trial. I found that the Parent Abstract class Policy.java should have following entries like
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
visible = true
)
#JsonSubTypes({
#JsonSubTypes.Type(value = APIKeyPolicy.class, name = "API_KEY"),
#Type(value = BasicAuth.class, name = "BASIC_AUTH")})
public abstract class Policy {
//with all methods
}
So, now when I would try to deserialize this to object I should be getting following
"API_KEY": {
"type": "API_KEY",
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"type": "BASIC_AUTH",
"username": "username",
"password": "password",
"policyEnabled": true
}
But it comes with an additional property as type which is fine as of now. as I am more interested on Policy Name ENUM with corresponding attributes.

How to exclude Weld metadata from JSON object serialization

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.

Tweak jackson polymorphic deserialization

I have a simple polymorphic model like this
public class Foo {
private Bar bar1;
private Bar bar2;
public Bar getBar1() {
return bar1;
}
public Bar getBar2() {
return bar2;
}
public void setBar1(Bar bar1) {
this.bar1 = bar1;
}
public void setBar2(Bar bar2) {
this.bar2 = bar2;
}
}
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#type")
public class Bar {
}
public class BarExpression extends Bar {
private String expression;
public String getExpression() {
return expression;
}
#JsonIgnore
public Object getValue() {
return null;
}
public void setExpression(String expression) {
this.expression = expression;
}
}
public class BarLiteral extends Bar {
private String value;
private String type;
public String getType() {
return type;
}
public Object getValue() {
return value;
}
public void setType(String type) {
this.type = type;
}
public void setValue(String value) {
this.value = value;
}
}
Serializing a simple example like this
public void run() throws Exception {
Foo foo;
BarLiteral bar1;
BarExpression bar2;
//
foo = new Foo();
bar1 = new BarLiteral();
bar1.setType("java.lang.String");
bar1.setValue("gnu");
foo.setBar1(bar1);
bar2 = new BarExpression();
bar2.setExpression("bean.property * 2");
foo.setBar2(bar2);
//
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
StringWriter w = new StringWriter();
mapper.writeValue(w, foo);
System.out.println(w.toString());
}
gives the expected result:
{
"bar1" : {
"#type" : "de.mit.jackson.BarLiteral",
"value" : "gnu",
"type" : "java.lang.String"
},
"bar2" : {
"#type" : "de.mit.jackson.BarExpression",
"expression" : "bean.property * 2"
}
}
The question is now: I want to improve user experience when handwriting this "DSL" by supporting "primitive shortcuts" for the typed "Bar..." classes like this
{
"bar1" : "gnu",
"bar2" : "#{bean.property * 2}"
}
The solution that came closest was using a converter on Foo#bar1 and Foo#bar2, checking for either String or "Bar" input, but this solution requires decoration of every attribute definition.
Creating a deserializer with a comparable behavior did not work, as the #JsonTypeInfo is not compatible in the sense that i can have a #JsonDeserialize implementation that will check for a String event and then delegate to the standard #JsonTypeInfo process. The #JsonTypeInfo standard will check only for the #type and then delegate back to the (subtype) deserializer which is again my wrapper implementation.
The required process is
if input event is string {
parse and return string input
} else {
activate #type parsing delegate;
after #type parsing activate standard BeanDeserializer
(**not** my implementation)
}
Is there another hook i am missing?