Apache Camel JSON Marshalling to POJO Java Bean - json

I think I have a simple question, but can't seem to figure it out.
I'm invoking a POJO with a class created from unmarshalling JSON as the parameter for the method. The question is, how do I marshal the return from the method back to JSON?
My route is below;
from("direct:start")
.choice()
.when(header("methodname").isEqualTo("listCases"))
.unmarshal().json(JsonLibrary.Jackson, UserDetails.class)
.to("bean:com.xxx.BeanA")
.when(header("methodName").isEqualTo("listPersons"))
.unmarshal().json(JsonLibrary.Jackson, CaseDetails.class)
.to("bean:com.xxx.BeanB");
...and I'm invoking the route by the below;
ProducerTemplate template = camelContext.createProducerTemplate();
template.setDefaultEndpoint(camelContext.getEndpoint("direct:start"));
InvocationResult result = (InvocationResult)template.requestBodyAndHeader(payload, "methodName", methodName);
Payload is JSON, and the methodName is either listCases or listPersons in this example.
My InvocationResult class is generic and contains a String returnCode attribute as well as an object reference to the object I would like to be converted to JSON. This object will be different depending on whether listCases or listPersons is executed.
Thanks,
Bic

My impression is that your actual issue isn't about marshalling (which should be entirely straightforward), but about processing a response after having routed the message using choice(). You need to close the choice() block using end() (assuming the result of each branch will be processed in the same way), then make sure the response gets written to the out message body in the last step of the route.
Anyway, here is an example I've just tested:
public class JacksonTestRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jetty:http://localhost:8181/foo").to("direct:foo");
from("direct:foo")
.unmarshal().json(JsonLibrary.Jackson, Foo.class)
.choice()
.when().simple("${body.foo} == 'toto'")
.log("sending to beanA")
.to("bean:beanA")
.otherwise()
.log("sending to beanB")
.to("bean:beanB")
// close the choice() block :
.end()
// per the javadoc for marshall(), "the output will be added to the out body" :
.marshal().json(JsonLibrary.Jackson);
}
}
public class Foo {
private String foo; // Constructor and accessor omitted for brevity
}
public class Bar1 {
private String bar1; // Constructor and accessor omitted for brevity
}
public class Bar2 {
private String bar2; // Constructor and accessor omitted for brevity
}
public class BeanA {
public Bar1 doSomething(final Foo arg) {
return new Bar1(arg.getFoo() + "A");
}
}
public class BeanB {
public Bar2 doSomething(final Foo arg) {
return new Bar2(arg.getFoo() + "B");
}
}
POSTing {"foo":"toto"} returns {"bar1":"totoA"} (and logs sending to beanA).
POSTing {"foo":"titi"} returns {"bar2":"titiB"} (and logs sending to beanB).

It is as simple as this .marshal().json(JsonLibrary.Jackson) (this is what you want)

Related

JSON parse error: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists)

I have a Spring Boot application using version 2.2.4 and Zulu Java 11.0.5 from Azul. It is accessing a REST web service which is deployed on a Payara web server (version 5.194).
I am using the following DTOs:
public class IdDTO extends BasicResponseDTO {
private long id;
public IdDTO(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and
public class BasicResponseDTO implements Serializable {
private String errorCode;
public BasicResponseDTO() {
this.setErrorCode(null);
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
I invoke a REST web service and from Postman I see I receive (correctly) the following response:
{
"errorCode": null,
"id": 3534016
}
However, when I retrieve the response, I get the following exception:
class org.springframework.web.client.RestClientException/Error while extracting response for type [class com.dto.IdDTO] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
Does anyone have any idea on why the application is not able to map the received JSON to the object?
P.S. 1) I also have other DTOs that extend BasicResponseDTO and the de-serialization works fine for them.
P.S. 2) The definition of the classes is the same on both the server and the client.
There is no default constructor on IdDTO. Only one that takes id:
public IdDTO(long id) {
this.id = id;
}
You have to add one:
public IdDTO() {
}
This is needed by JSON deserialization to construct objects from your classes
This has already been answered here: JSON parse error: Can not construct instance of io.starter.topic.Topic
In general design wise, I would strongly recommend going for the second solution with annotating the constructor, rather than a default argument constructor. That way you can have immutable fields (make them final) and ensure that you don't accept nulls. In 2020 I would consider any design using a default constructor due to serialization legacy-design.

JSON Mapping Exception while calling post method with request body

I have a controller with the below mentioned contract ---
#RequestMapping(value="/api/devices/certs",method = RequestMethod.POST,consumes={"application/json","application/xml"})
public String submitCertificate(#RequestBody Certificate certificate){
System.out.println(certificate.getBase64String());
return certificate.getBase64String();
}
Other than this there are two pojo classes --
1)
public class DeviceCertificateRequest implements Serializable {
private static final long serialVersionUID = -4408117936126030294L;
private Certificate certificate;
public Certificate getCertificate() {
return certificate;
}
public void setCertificate(Certificate certificate) {
this.certificate = certificate;
}
#Override
public String toString() {
return "DeviceCertifficateRequest [certificate=" + certificate + "]";
}
}
2)
public class Certificate implements Serializable {
private static final long serialVersionUID = 4044105355620137636L;
private String base64String;
public String getBase64String() {
return base64String;
}
public void setBase64String(String base64String) {
this.base64String = base64String;
}
#Override
public String toString() {
return "Certificate [base64String=" + base64String + "]";
}
}
Now I am using spring boot and have added jackson-data-bind dependency for content negotiation, also I wanted to consume both json as well as xml data as an input and thus mapping it to the POJO file.
but I am not able to attain the desired result, even I am getting below mentioned error in the logs when trying to send across json from a rest client.
Error----
ERROR] 2017-02-07 13:48:45.448 [http-nio-8080-exec-1] ConfigManagerExceptionHandler - exception while accessing url:-http://localhost:8080/api/devices/certserror message:-Could not read document: Can not construct instance of com.lufthansa.configmanager.request.beans.Certificate: no String-argument constructor/factory method to deserialize from String value ('DeviceCertificateRequest')
at [Source: java.io.PushbackInputStream#3c891128; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.lufthansa.configmanager.request.beans.Certificate: no String-argument constructor/factory method to deserialize from String value ('DeviceCertificateRequest')
at [Source: java.io.PushbackInputStream#3c891128; line: 1, column: 1]
Json send across --
"certificate": {
"base64String": "abc"
}
Please also let me know whether it will work properly for xml payload as well, as I want to consume both xml as well as json input
Show us how do You make a request and double check the names of variables.
Check and recheck if You have the correct IMPORTS in the controller, if the Certificate is actually from Your package and not any other.
Add
#JsonInclude(Include.NON_NULL)
class Foo{}
so You won`t have null problems.
Delete for testing the serialVersionUID from certificate.
Try to add #ResponseBody to You consuming controller method.
Try to send
{
"base64String": "abc"
}
without the variable name.
I worked by creating parametrised constructor in the POJO class, seems it jackson data bind requires a parametrised constructor for object creation.
Still have to check for xml input though.

JAX-RS - JSON List to Object with JaxB

I am using JAX-RS (CXF) with JaxB and Jackson to provide a REST-API.
Unfortunately, none of the found results helps me with following (simple) problem:
I implemented following method:
#POST
#Path(ApiStatics.ARMY_CREATE_ARMY)
public com.empires.web.dto.Army createArmy(#FormParam("locationid") long locationId, #FormParam("name") String name, #FormParam("troops") ArmyTroops troops) {
and here are is my model class:
#XmlRootElement
#XmlSeeAlso(ArmyTroop.class)
public class ArmyTroops {
public ArmyTroops() {
}
public ArmyTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
#XmlElement(name = "troops")
private List<ArmyTroop> troops = new ArrayList<ArmyTroop>();
public List<ArmyTroop> getTroops() {
return troops;
}
public void setTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
}
ArmyTroop
#XmlRootElement(name = "troops")
public class ArmyTroop {
#XmlElement
private long troopId;
#XmlElement
private String amount;
public long getTroopId() {
return troopId;
}
public void setTroopId(long troopId) {
this.troopId = troopId;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
}
My json that i send looks like this:
locationid 1
name asdasd
troops {"troops":[{"troopId":4,"amount":"5"},{"troopId":6,"amount":"5"}]}
Unfortunately, the object gets not transformed. Instead I receive this error:
InjectionUtils #reportServerError - Parameter Class com.empires.web.dto.in.ArmyTroops has no constructor with single String parameter, static valueOf(String) or fromString(String) methods
If I provide the constructor with a single string parameter, I get passed the whole json string for "troops" as mentioned above.
Any ideas why JaxB does not work at this point?
You are passing all your parameters with #Form annotation.
But the Form part of the http message must be an xml data structure.
Your 3 parameters don't have a main xml datastructure so it wont work.
In short, form params are send as body.
Cxf use the MultivaluedMap to send params (cxf have an xml model for this structure).
As you can see it is not fit for parameters that can't be trivally serialized.
Here me solution would be to drop the #FormParam to avoid the problem:
1) Use #PathParam #CookieParam to send yours first 2 parameters, and the 'no tag' (body) only for the army compositions.
2) Define an uber object that take all parameters and can be serialized as xml datastructure and use the 'no tag' (body) sending.
3) Use soap, with cxf it is really easy to gets both Rest and Soap.

Parse unnamed mappings in JSON using Jackson

I have some JSON in the following format that I'm trying to parse with Jackson -
"response":{
"response_inner":{
"a":{"field1":2,"field2":0,"field3":5,"field4":0,"field5":[{"field5_1":"b","field5_2":1},{"field5_1":"c","field5_2":1}]},
"d":{"field1":2,"field2":6,"field3":11,"field4":0,"field5":[{"field5_1":"c","field5_2":1},{"field5_1":"b","field5_2":1}]},
"response_inner_bool":false
}
}
Here "a", "b" etc. are some Strings that can change in each response.
I've created a Java object to represent the 'response_inner' (let's call it ResponseInner) and another to represent the object containing the field?s (let's call this one FieldInfo) but I'm not sure how to parse this using the #JsonCreator and #JsonProperty annotations - ResponseInner objects can contain any number of String -> FieldInfo mappings.
I tried parsing it like this -
public class Response {
private ResponseInner responseInner;
#JsonCreator
public Response(#JsonProperty("response_inner") ResponseInner responseInner) {
this.reponseInner = responseInner;
}
}
public class ResponseInner {
private Map<String, FieldInfo> stringToFieldInfoMap;
private boolean responseInnerBool;
#JsonCreator
public ResponseInner(Map<String, FieldInfo> stringToFieldInfoMap, #JsonProperty("response_inner_bool") boolean responseInnerBool ) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.responseInnerBool = responseInnerBool;
}
}
But it complains that Argument #0 of constructor has no property name annotation; must have name when multiple-paramater constructor annotated as Creator. Any suggestions for how to get around this?
You don't seem to be using the stringToFieldInfoMap within ResponseInner anyway. Why do you need to pass it as parameter?
If you do need it in that class, you can simply set it via a setter rather than passing it to constructor.
Alternatively, you could perhaps utilize a third class which deals with that actual mapping of the response, which consumes the Response object (which would in turn consume the ResponseInner object which has had the Map removed from it). This would actually allow you to decouple the mapping logic from the response logic perhaps.
public class MappedResponse {
private Map<String, FieldInfo> stringToFieldInfoMap;
private Response response;
public MappedResponse(Map<String, FieldInfo> stringToFieldInfoMap, Response response) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.response = response;
}
}

Newtonsoft json serializer returns empty object

Ok - I've been beating my head against this for a few of hours now. Time to ask for help.
I have just upgraded my Web application project to ASP.NET MVC 4 RC, and the new WebApi.
My web api method is now returning EMPTY json "{}" - even though my object is fully populated.
I have replace the serializer with my own MediaTypeFormatter that also calls the Newtonsoft Json serializer, just so I can hook in and see things working.
What I see is an object going in to the serializer, and coming out as "{}".
This USED to work before I upgraded.
This is my object
[Serializable]
public class Parameters
{
public string ApplicantName { get; set; }
}
And I am just calling:
var result = JsonConvert.SerializeObject(new Parameters(){ Name = "test" });
I get back
"{}"
Whats going on??
[EDIT]
Someone else having the same problem... after running through the Newtonsoft source code, I can see we're having the exact same problem from a recent change.
http://json.codeplex.com/discussions/357850
Ok - there have been numerous changes, which result is some pretty radical changes to the Json output. These changes also include how custom TypeConverters are applied.
I have written a basic resolver which (for us at least) causes the Newtonsoft serializer to behave more like a basic Serializable object serializer - i.e. serializes all PROPERTIES, and doesnt use custom TypeConverters...
/// <summary>
/// A resolver that will serialize all properties, and ignore custom TypeConverter attributes.
/// </summary>
public class SerializableContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
foreach (var p in properties)
p.Ignored = false;
return properties;
}
protected override Newtonsoft.Json.Serialization.JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
if (contract is Newtonsoft.Json.Serialization.JsonStringContract)
return CreateObjectContract(objectType);
return contract;
}
}
* REGISTRATION *
In your MvcApplication "Application_Start"...
GlobalConfiguration.Configuration.Formatters
.JsonFormatter.SerializerSettings.ContractResolver =
new SerializableContractResolver()
{
IgnoreSerializableAttribute = true
};