Jaxb annotations to deserialize json data using Jackons - json

I do have a simple json data and my pojo is annotated with jaxb annotations. I wanted to deserialize it WITHOUT using json annotations.
(It's must, please do NOT suggest to use both annotations on pojo).
Jackson confirms (that's what I understood) that it's possible through
http://wiki.fasterxml.com/JacksonJAXBAnnotations
https://github.com/FasterXML/jackson-module-jaxb-annotations
But when I ran the code, it does NOT provide the desired result.
Here is the pojo.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Simple_root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Simple {
#XmlElement(name = "x")
private String firstName;
#XmlElement(name = "y")
private String lastName;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Below is the test class with two test methods using jackson ObjectMapper.
import org.junit.Assert;
import org.junit.Test;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
public class XmlMapperTest {
#Test
public void jsonDeserialize_using_JaxbAnnotationIntrospector() throws Exception {
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
jsonMapper.getDeserializationConfig().with(introspector);
jsonMapper.getSerializationConfig().with(introspector);
Simple simple = jsonMapper.readValue("{\"Simple_root\": {\"x\": \"2\", \"y\": \"4\"}}", Simple.class);
Assert.assertNotNull(simple.getFirstName());
Assert.assertNotNull(simple.getLastName());
}
#Test
public void jsonDeserialize_using_JaxbAnnotationModule() throws Exception {
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JaxbAnnotationModule jaxbModule = new JaxbAnnotationModule();
jsonMapper.registerModule(jaxbModule);
Simple simple = jsonMapper.readValue("{\"Simple_root\": {\"x\": \"2\", \"y\": \"4\"}}", Simple.class);
Assert.assertNotNull(simple.getFirstName());
Assert.assertNotNull(simple.getLastName());
}
}
Any help would be appreciated, as I know I am definitely missing something which I couldn't find out in a day and 2 hours before bedtime.

Related

Serializing and Deserializing Lambda with Jackson

I am trying to serialise and deserialise a class RuleMessage but can't get it to work. Here is my code:
public class RuleMessage {
private String id;
private SerializableRunnable sRunnable;
public RuleMessage(String id, SerializableRunnable sRunnable) {
this.id = id;
this.sRunnable = sRunnable;
}
}
public interface SerializableRunnable extends Runnable, Serializable {
}
#Test
public void testSerialization() throws JsonProcessingException {
MAPPER.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
SerializableRunnable r = () -> System.out.println("Serializable!");
RuleMessage rule = new RuleMessage("1", r);
System.out.println(MAPPER.writeValueAsString(businessRule));
}
I am using Java 8. Can someone tell me if this is possible in the Jackson library?
Jackson was created to keep object state not behaviour. This is why it tries to serialise POJO's properties using getters, setters, etc. Serialising lambdas break this idea. Theres is no any property to serialise, only a method which should be invoked. Serialising raw lambda object is really bad idea and you should redesign your app to avoid uses cases like this.
In your case SerializableRunnable interface extends java.io.Serializable which gives one option - Java Serialisation. Using java.io.ObjectOutputStream we can serialise lambda object to byte array and serialise it in JSON payload using Base64 encoding. Jackson supports this scenario providing writeBinary and getBinaryValue methods.
Simple example could look like below:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class JsonLambdaApp {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
SerializableRunnable action = () -> System.out.println("Serializable!");
String json = mapper.writeValueAsString(new RuleMessage("1", action));
System.out.println(json);
RuleMessage ruleMessage = mapper.readValue(json, RuleMessage.class);
ruleMessage.getsRunnable().run();
}
}
#JsonSerialize(using = LambdaJsonSerializer.class)
#JsonDeserialize(using = LambdaJsonDeserializer.class)
interface SerializableRunnable extends Runnable, Serializable {
}
class LambdaJsonSerializer extends JsonSerializer<SerializableRunnable> {
#Override
public void serialize(SerializableRunnable value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream)) {
outputStream.writeObject(value);
gen.writeBinary(byteArrayOutputStream.toByteArray());
}
}
}
class LambdaJsonDeserializer extends JsonDeserializer<SerializableRunnable> {
#Override
public SerializableRunnable deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
byte[] value = p.getBinaryValue();
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(value);
ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream)) {
return (SerializableRunnable) inputStream.readObject();
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
}
class RuleMessage {
private String id;
private SerializableRunnable sRunnable;
#JsonCreator
public RuleMessage(#JsonProperty("id") String id, #JsonProperty("sRunnable") SerializableRunnable sRunnable) {
this.id = id;
this.sRunnable = sRunnable;
}
public String getId() {
return id;
}
public SerializableRunnable getsRunnable() {
return sRunnable;
}
}
Above code prints JSON:
{
"id" : "1",
"sRunnable" : "rO0ABXNyACFqYXZhLmxhbmcuaW52b2tlLlNlcmlhbGl6ZWRMYW1iZGFvYdCULCk2hQIACkkADmltcGxNZXRob2RLaW5kWwAMY2FwdHVyZWRBcmdzdAATW0xqYXZhL2xhbmcvT2JqZWN0O0wADmNhcHR1cmluZ0NsYXNzdAARTGphdmEvbGFuZy9DbGFzcztMABhmdW5jdGlvbmFsSW50ZXJmYWNlQ2xhc3N0ABJMamF2YS9sYW5nL1N0cmluZztMAB1mdW5jdGlvbmFsSW50ZXJmYWNlTWV0aG9kTmFtZXEAfgADTAAiZnVuY3Rpb25hbEludGVyZmFjZU1ldGhvZFNpZ25hdHVyZXEAfgADTAAJaW1wbENsYXNzcQB+AANMAA5pbXBsTWV0aG9kTmFtZXEAfgADTAATaW1wbE1ldGhvZFNpZ25hdHVyZXEAfgADTAAWaW5zdGFudGlhdGVkTWV0aG9kVHlwZXEAfgADeHAAAAAGdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAHZyABxjb20uY2Vsb3hpdHkuSnNvblR5cGVJbmZvQXBwAAAAAAAAAAAAAAB4cHQAIWNvbS9jZWxveGl0eS9TZXJpYWxpemFibGVSdW5uYWJsZXQAA3J1bnQAAygpVnQAHGNvbS9jZWxveGl0eS9Kc29uVHlwZUluZm9BcHB0ABZsYW1iZGEkbWFpbiQ1YzRiNmEwOCQxcQB+AAtxAH4ACw=="
}
and lambda:
Serializable!
See also:
How to serialize a lambda?
How to serialize a lambda function in Java?
First, in RuleMessage you have to either create getters / setters or make the fields public in order to provide Jackson access to the fields.
Your code then prints something like this:
{"#class":"RuleMessage","id":"1","sRunnable":{"#class":"RuleMessage$$Lambda$20/0x0000000800b91c40"}}
This JSON document cannot be deserialized because RuleMessage has no default constructor and the lambda cannot be constructed.
Instead of the lambda, you could create a class:
public class Runner implements SerializableRunnable {
#Override
public void run() {
System.out.println("Serializable!");
}
}
and construct your pojo like this:
new RuleMessage("1", new Runner())
The Jackson deserializer is now able to reconstruct the objects and execute the runner.

spring boot kafka generic JSON templateSender

I am working with kafka and spring boot and I need to send JSON object to kafka, the point is that I am able to send an object as JSON configuring KafkaTemplate but just for this object.
package com.bankia.apimanager.config;
import com.bankia.apimanager.model.RequestDTO;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.support.serializer.JsonSerializer;
import java.util.HashMap;
import java.util.Map;
#Configuration
public class KafkaConfiguration {
#Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return props;
}
#Bean
public ProducerFactory<String, RequestDTO> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
#Bean
public KafkaTemplate<String, RequestDTO> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
package com.bankia.apimanager.controller;
import com.bankia.apimanager.model.RequestDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/infrastructure")
public class InfraStructureRequestController {
private final static Logger LOG = LoggerFactory.getLogger( InfraStructureRequestController.class );
private static final String TOPIC = "test";
#Autowired
private KafkaTemplate<String, RequestDTO> sender;
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String postMessage(){
ListenableFuture<SendResult<String, RequestDTO>> future = sender.send(TOPIC, new RequestDTO("Hola","Paco"));
future.addCallback(new ListenableFutureCallback<SendResult<String, RequestDTO>>() {
#Override
public void onSuccess(SendResult<String, RequestDTO> result) {
LOG.info("Sent message with offset=[" + result.getRecordMetadata().offset() + "]");
}
#Override
public void onFailure(Throwable ex) {
LOG.error("Unable to send message due to : " + ex.getMessage());
}
});
return "OK";
}
}
but what about if now I want to send a new DTO object? do I have to declare a new KafkaTemplate<String,NEWOBJECT> and autowire each kafka template declared in configuration for each object? there is another way to be able to just declare one kafkaTemplate in which I can send any type of object and automatically will be serialized in JSON?
I think, you can specify a generic KafkaTemplate<String, Object> and set the producer value serializer to JsonSerializer like this:
#Configuration
public class KafkaConfiguration {
#Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return props;
}
#Bean
public ProducerFactory<String, Object> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
#Bean
public KafkaTemplate<String, Object> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
Referring your code:
Value Serializer is correctly defined as JsonSerializer, which will convert objects of any type to JSON.
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return props;
}
Change <String, RequestDTO> to <String, Object> at every place in KafkaConfig & Controller.
Keep in mind that generics remain until compile time (type erasure)
only.
There are two scenario:
Scenario #1
If you want to use KafkaTemplate to send any type(as mentioned in your question) to kafka, so there is no need to declare your own KafkaTemplate bean because Spring boot did this for you in KafkaAutoConfiguration.
package org.springframework.boot.autoconfigure.kafka;
...
#Configuration(proxyBeanMethods = false)
#ConditionalOnClass(KafkaTemplate.class)
#EnableConfigurationProperties(KafkaProperties.class)
#Import({ KafkaAnnotationDrivenConfiguration.class, KafkaStreamsAnnotationDrivenConfiguration.class })
public class KafkaAutoConfiguration {
private final KafkaProperties properties;
public KafkaAutoConfiguration(KafkaProperties properties) {
this.properties = properties;
}
#Bean
#ConditionalOnMissingBean(KafkaTemplate.class)
public KafkaTemplate<?, ?> kafkaTemplate(ProducerFactory<Object, Object> kafkaProducerFactory,
ProducerListener<Object, Object> kafkaProducerListener,
ObjectProvider<RecordMessageConverter> messageConverter) {
KafkaTemplate<Object, Object> kafkaTemplate = new KafkaTemplate<>(kafkaProducerFactory);
messageConverter.ifUnique(kafkaTemplate::setMessageConverter);
kafkaTemplate.setProducerListener(kafkaProducerListener);
kafkaTemplate.setDefaultTopic(this.properties.getTemplate().getDefaultTopic());
return kafkaTemplate;
}
}
**Some Note**:
This config class has been annotated with #ConditionalOnClass(KafkaTemplate.class) that means: (from spring docs--->) #Conditional that only matches when the specified classes are on the classpath.
kafkaTemplate bean method is annotated with
#ConditionalOnMissingBean(KafkaTemplate.class) that means: (from spring docs ---->) #Conditional that only matches when no beans meeting the specified requirements are already contained in the BeanFactory.
Important! In pure java world, KafkaTemplate<?, ?> is not subtype of for example: KafkaTemplate<String, RequestDTO> so you can't to do this:
KafkaTemplate<?, ?> kf1 = ...;
KafkaTemplate<String, RequestDTO> kf2 = kf1; // Compile time error
because java parameterized types are invariant as mentioned in Effective Java third edition item 31. But is spring world that is ok and will be injected to your own service. You need only to specify your own generic type on your kafkaTemplate properties.
For example:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
#Service
public class KafkaService {
#Autowired
private KafkaTemplate<Integer, String> kafkaTemplate1;
#Autowired
private KafkaTemplate<Integer, RequestDTO> KafkaTemplate2;
}
Scenario #2
If you need to restrict value type of kafka record then you need to specify your own kafka bean something like this:
#Configuration(proxyBeanMethods = false)
#ConditionalOnClass(KafkaTemplate.class)
#EnableConfigurationProperties(CorridorTracingConfiguration.class)
public class CorridorKafkaAutoConfiguration {
#Bean
#ConditionalOnMissingBean(KafkaTemplate.class)
public KafkaTemplate<?, AbstractMessage> kafkaTemplate(ProducerFactory<Object, AbstractMessage> kafkaProducerFactory,
ProducerListener<Object, AbstractMessage> kafkaProducerListener,
ObjectProvider<RecordMessageConverter> messageConverter) {
KafkaTemplate<Object, AbstractMessage> kafkaTemplate = new KafkaTemplate<>(kafkaProducerFactory);
messageConverter.ifUnique(kafkaTemplate::setMessageConverter);
kafkaTemplate.setProducerListener(kafkaProducerListener);
kafkaTemplate.setDefaultTopic(this.properties.getTemplate().getDefaultTopic());
return kafkaTemplate;
}
Now this can be injected only to
KafkaTemplate<String, AbstractMessage> kafkaTemplate, the key type can be anything else instead of String. But you can send any sub type of AbstractMessage to kafka via it.
An example usage:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
#Service
public class KafkaService {
#Autowired
private KafkaTemplate<String, AbstractMessage> kafkaTemplate;
public void makeTrx(TrxRequest trxRequest) {
kafkaTemplate.send("fraud-request", trxRequest.fromAccountNumber(), new FraudRequest(trxRequest));
}
}
#Accessors(chain = true)
#Getter
#Setter
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class FraudRequest extends AbstractMessage {
private float amount;
private String fromAccountNumber;
private String toAccountNumber;
...
}
To restrict the key of kafka message follow the same (above) way

how to handle Json body in post request in jax-rs

I have a project (homework) about JAX-RS. I'm working with NetBeans, Jersey and Tomcat.
This is my "User" class for main object in the system.
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="user")
public class User {
//#XmlElement
//public int id ;
#XmlElement
public String username;
#XmlElement
public String fullname;
#XmlElement
public String gender;
#XmlElement
public String birthDate;
public User(){
}
public User(String username,String fullname, String gender,String birthDate){
//this.id = id;
this.username = username;
this.fullname = fullname;
this.gender = gender;
this.birthDate = birthDate;
}
}
This is my "JAXBContextResolver" Class
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext>{
private JAXBContext context;
private Class[] types = {User.class};
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext( JSONConfiguration.mapped().build(), types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
And this is my post method in the "UserService" class
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public List<User> createNewUser(User tUser) {
List<User> list = new ArrayList<User>();
list.add(tUser);
return list;
}
When I am trying a post new user in the localhost with RESTClient (Firefox add-ons) my request body is a json input like that:
{"user":{"username":"blabla","fullname":"blabla","gender":"M","birthDate":"05.01.1978"}}
In the post method (in the UserService class) must the variable "tUser" automatically filled with the coming input ? "tUser" variable shows null elements in it in the debugging mode like that:
If I know wrong could somebody correct me please? Why this values shows null? Must not them shows "blabla" - "blabla" - "M" - "05.01.1878" ? Could you help me please?
I solved this problem; In the JAXBContextResolver class I change the method like that :
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext( JSONConfiguration.mapped().rootUnwrapping(false).build(), types);
}
The difference with the first one is adding "rootUnwrapping(false)" expression.
#XmlRootElement is not working in your example. Send
{"username":"blabla","fullname":"blabla","gender":"M","birthDate":"05.01.1978"}
instead
EDIT
1)
public List<User> createNewUser(Request tUser)
and class
class Request
{
public User user;
}
2)
public List<User> createNewUser(String tUser)
and convert String to object using google-gson or jackson json processor

org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class models.Job] from JSON String

i use the playframework and tried to deserialize some json into a java object.
It worked fine, exept the relationship in the model. I got the following exception
enter code hereorg.codehaus.jackson.map.JsonMappingException: Can not
instantiate value of type [simple type, class models.Job] from JSON
String; no single-String constructor/factory method (through reference
chain: models.Docfile["job"])
i thought jackson in combination with play could do that:
this is the json
{"name":"asd","filepath":"blob","contenttype":"image/png","description":"asd","job":"1"}
and this my code, nothing special:
public static Result getdata(String dataname) {
ObjectMapper mapper = new ObjectMapper();
try {
Docfile docfile = mapper.readValue((dataname), Docfile.class);
System.out.println(docfile.name);
docfile.save();
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return ok();
}
Hope there is help for me, thanks
Markus
UPDATE:
Docfile Bean:
package models;
import java.util.*;
import play.db.jpa.*;
import java.lang.Object.*;
import play.data.format.*;
import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import play.data.validation.Constraints.*;
import play.data.validation.Constraints.Validator.*;
import javax.persistence.*;
import com.avaje.ebean.Page;
#Entity
public class Docfile extends Model {
#Id
public Long id;
#Required
public String name;
#Required
public String description;
public String filepath;
public String contenttype;
#ManyToOne
public Job job;
public static Finder<Long,Docfile> find = new Model.Finder(
Long.class, Docfile.class
);
public static List<Docfile> findbyJob(Long job) {
return find.where()
.eq("job.id", job)
.findList();
}
public static Docfile create (Docfile docfile, Long jobid) {
System.out.println(docfile);
docfile.job = Job.find.ref(jobid);
docfile.save();
return docfile;
}
}
Either you change your JSON in order to describe your "job" entity :
{
"name":"asd",
"filepath":"blob",
"contenttype":"image/png",
"description":"asd",
"job":{
"id":"1",
"foo", "bar"
}
}
or you create a constructor with a String parameter in your Job bean:
public Job(String id) {
// populate your job with its id
}
when limited time +ee: +jax-rs && +persistence, +gson; I have solved it then as:
#Entity
#XmlRootElement
#Table(name="element")
public class Element implements Serializable {
public Element(String stringJSON){
Gson g = new Gson();
Element a = g.fromJson(stringJSON, this.getClass());
this.setId(a.getId());
this.setProperty(a.getProperty());
}
public Element() {}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
...
}

Binding a Generic Java Class to a JSON using JAXB

I have the following java class
#XmlRootElement
#XmlSeeAlso(DataClass.class)
public static class EnvelopeClass<T> {
#XmlElement
public String version;
#XmlElement
public T data;
EnvelopeClass() {
}
EnvelopeClass(String version, T data) {
this.version = version;
this.data = data;
}
}
#XmlRootElement
public static class DataClass {
#XmlElement
public String name;
DataClass() {
}
DataClass(String name) {
this.name = name;
}
}
I'm creating its instance and marshaling it to json
EnvelopeClass<DataClass> dataClassEnvelopeClass = new EnvelopeClass<DataClass>("1.0", new DataClass("myName"));
I have next result:
{"version":"1.0","data":{"#type":"dataClass","name":"myName"}}
I do not want to have type type information in the json "#type":"dataClass", in other words I want to have next result:
{"version":"1.0","data":{"name":"myName"}}
Exactly this result I have when EnvelopeClass doesn't have Generics.
Is there a way to do this?
To get the desired behaviour, you can use #XmlAnyElement on the data property instead of #XmlElement. For the #XmlAnyElement property the value will correspond to a class with the matching #XmlRootElement annotation.
EnvelopeClass
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlRootElement
#XmlSeeAlso(DataClass.class)
public class EnvelopeClass<T> {
#XmlElement
public String version;
#XmlAnyElement
public T data;
EnvelopeClass() {
}
EnvelopeClass(String version, T data) {
this.version = version;
this.data = data;
}
}
DataClass
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="data")
public class DataClass {
#XmlElement
public String name;
DataClass() {
}
DataClass(String name) {
this.name = name;
}
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(EnvelopeClass.class);
DataClass data = new DataClass("myName");
EnvelopeClass envelope = new EnvelopeClass<DataClass>("1.0", data);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(envelope, System.out);
}
}