Camel Bindy Model Inheritance - csv

I'm trying to have some inheritance in using bindy csv Dataformat (Camel 2.13.1)
I have an abstract class and two concrete classe, each one inherit the base class.
ex
#CsvRecord(separator=";",generateHeaderColumns=true,skipFirstLine=true)
public abstract class AbstractBaseFormat {
#DataField(columnName="FIELD1",pos=1)
protected String field1;
#DataField(columnName="FIELD2",pos=2)
protected String field2;
// getter/setter
}
public class Format1 extends AbstractBaseFormat {
#DataField(columnName="FIELD3",pos=3)
private String field3;
// getter / setter
}
public class Format2 extends AbstractBaseFormat {
#DataField(columnName="FIELD3",pos=3)
private Long field3;
// getter / setter
}
When i use concrete classe in my route , camel throw an error
java.lang.IllegalArgumentException: The separator has not been defined in the annotation #CsvRecord or not instantiated during initModel. must be specified
Ok, the #CsvRecord annotation is on the abstract class and is not recognized by the Bindy Factory.
But if i put this annotation on each concrete class , i get another exception as Bindy didn't find the first field (pos = 1).
Can i use this kind of model with Bindy csv Dataformat ?

Related

Blaze Persistence EntityView inheritance mapping

I'm currently using Quarkus combined with Blaze Persistence for my microservice. I have the following entity model:
#Entity
public class Content extends BaseEntity {
private boolean deleted;
private boolean published;
}
#Entity
public class WebContent extends Content {
private String webpage;
}
I've mapped the entities to the following EntityViews:
#EntityView(Content.class)
#EntityViewInheritance
#CreatableEntityView
#UpdatableEntityView
public interface ContentUpdateView {
#IdMapping
Long getId();
boolean isPublished();
void setPublished(boolean published);
}
#EntityView(WebContent.class)
#CreatableEntityView
#UpdatableEntityView
public interface WebContentUpdateView extends ContentUpdateView {
String getWebpage();
void setWebpage(String webpage);
}
I have the following method in my ContentsResource:
#POST
public ContentUpdateView save(ContentUpdateView content) {
return contentsService.save(content);
}
When I invoke the post operation I only get the base ContentUpdateView and not the WebContentUpdateView. Is there any configuration to do? (with Jackson I use #JsonTypeInfo and #JsonSubType annotation on the entities to accomplish this).
Thanks
euks
I got it working using Jackson annotation. Here's the base class:
#EntityView(Content.class)
#EntityViewInheritance
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = DescriptiveContentView.class, name = "descriptive"),
#JsonSubTypes.Type(value = MediaContentView.class, name = "media"),
#JsonSubTypes.Type(value = WebContentView.class, name = "web")
})
#JsonTypeName("content")
public abstract class ContentView {
#IdMapping
public abstract Long getId();
public abstract boolean isPublished();
}
And here's a subclass:
#EntityView(DescriptiveContent.class)
#JsonTypeName("descriptive")
public abstract class DescriptiveContentView extends ContentView {
public abstract Set<LocalizedParagraphView> getLocalizedParagraphs();
}
I'm using abstract classes for other purposes, but it also works with interfaces.

jsonencoderdecoder without getter/setter method

I used the RestyGWT's JsonEncoderDecoder interface to encode/decode some objects. Among them there are instances of classes having properties not exposed using getter/setter methods. I tried annotating corresponding properties with org.codehaus.jackson.annotate.JsonProperty. But it's not working, causing error
[ERROR] [jsonsample] - field must not be private: com.mycompany.jsonsample.ItemList.items
com.mycompany.jsonsample.ItemList is the class with property items which has no getter/setter and annotated as said above.
Also is it possible to tell the encoder/decoder to skip some properties?
Example with private field and annotated constructor, you should provide more info on your problem though.
public abstract class Parent
{
#JsonCreator
public Parent(#JsonProperty("name") String name)
{
this.name = name;
}
#Override
public String getName()
{
return name;
}
private String name;
}

How to create type id using Custom Serilizer?

I'm making a DB replication mechanism. I just want to send over an object once over the network. I have implemented a custom serializer that makes a reference to the object using an UUID instead of serializing the object itself. On the other side I plan to just look up the UUID in DB in order to create the none serialized object.
The problem I have is that Jackson expects a type id from my custom serializer. How do I create a type id?
I got the follwing class:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="objectType")
public class Brand implements Serializable {
private String uuid;
#JsonSerialize(using = EntitySerializer.class)
private Producer producer = null;
// Omitting getters and setters
}
and
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="objectType")
public class Producer extends Entity implements Serializable {
private String uuid;
private String name;
//Omitting getters and setters.
}
To serialize Producer i got the following serializer:
public class EntitySerializer extends JsonSerializer<Entity> {
#Override
public void serialize(Entity value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeString(value.getUUID());
}
}
When I serialize I get the following error because I don't generate any #JsonTypeInfo type Id.
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type com.fodolist.model.Producer (through reference chain: com.fodolist.server.callprocessor.SyncContainter["one"]->com.fodolist.model.Brand["producer"])
Is there any better way to solve the problem?
Maybe have a look at #JsonIdentityInfo; one of id generators creates UUIDs to use as ids. By default the first instance will be serialized in its entirety (otherwise it is not possible to deserialize the data), but you can change that by using #JsonIdentityReference to indicate that all instances are to be serialized using just the id (generated, or accessed via property).
In case the uuid field is already set, you can use the PropertyGenerator as follow:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class Producer extends Entity {
....
}

JAXB Annotated Classes for JSON (Jackson) containing abstract property

With the following classes, which make up the JSON message structure I transfer to the client side after a JAX-RS call (CXF), I receive
org.apache.cxf.jaxrs.client.ClientWebApplicationException: .Problem with reading the response message, class : class bg.vivacom.sel.dto.SELResponse, ContentType : application/json.
...
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of bg.vivacom.sel.dto.SELDTO, problem: abstract types can only be instantiated with additional type information
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#16edbe39; line: 1, column: 157] (through reference chain: bg.vivacom.sel.dto.SELResponse["dto"])
The returned object contains property dto which is an interface
#XmlRootElement(name = "SELResponse")
public class SELResponse implements Serializable{
#XmlElement(name = "corelationID")
private String corelationID;
#XmlElement(name = "responseTimeStamp")
private String responseTimeStamp;
#XmlElement(name = "respStatusCode")
private String respStatusCode;
#XmlElement(name = "respStatusMessage")
private String respStatusMessage;
#XmlElement(name = "processStatus")
private ProcessStatus processStatus;
#XmlElement(name = "dto")
private SELDTO dto;
The interface
public interface SELDTO extends Serializable {
is a way to include a number of different DTOs in my answer depending on the request, such as
#XmlRootElement(name = "customerProfile")
public class CustomerProfileDTO implements SELDTO {
#XmlElement(name = "customerCode")
private String customerCode;
...
and
#XmlRootElement(name = "sms")
public class SmsDTO implements SELDTO {
#XmlElement(name = "From")
private String from;
...
Any ideas how else I have to annotate the classes so that the response can be correctly set to the specific object type. I understand that it requires additional information as at the time it re-creates the dto object it doesn't know its type so I have tried to annotate the interface as follows:
#XmlSeeAlso({CustomerProfileDTO.class, SmsDTO .class})
public interface SELDTO extends Serializable {
but I still get the issue. Any thoughts would be appreciated it.
I would recommend using Jackson annotation '#JsonTypeInfo' for this case.
But if you must use JAXB annotations, use #XmlElements or #XmlElementRefs similar to how you'd use them with JAXB/XML:
#XmlElements({
#XmlElement(type=SubClass1.class, name="sub1"),
#XmlElement(type=SubClass2.class, name="sub2")
})
note that you must include specific mapping to possible subtypes here.

Jackson with JAXB - abstract types instantiation

I'm facing problem with Jackson's ObjectMapper using JAXB annotations. To be concrete, I'm having collection with interface generic information and although I can deserialize input from XML, it is not possible with Jackson (using JAXB introspector). Maybe I'm just missing some configuration property or JAXB annotation? The problem is that "abstract types can only be instantiated with additional type information", I thought #XmlElementRef (or #XmlElement) with type information will handle this problem, but obviosly it does not.
Please note, that I want to stay only with JAXB annotations if possible.
E.g. using #JsonTypeInfo or #JsonDeserialize would be the last thing to do.
IEntry.java:
#XmlSeeAlso(Entry.class)
public interface IEntry {
String getValue();
}
Entry.java:
#XmlRootElement(name = "entry")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Aggregator.java:
#XmlRootElement(name = "aggregator")
public class Aggregator {
#XmlElementRef(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
Test method:
#Test
public void testSerialization() throws Exception {
ObjectMapper om = new ObjectMapper();
AnnotationIntrospector intr = new JaxbAnnotationIntrospector();
om.getDeserializationConfig().withAnnotationIntrospector(intr);
String json = "{\"entries\":[{\"value\":\"X\"},{\"value\":\"Y\"},{\"value\":\"Z\"}]}\";\n}";
Aggregator agr = om.readValue(json, Aggregator.class);
}
Thanks for all response
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I am not sure if Jackson supports this use case or not, but you appear to be using #XmlElementRef incorrectly. When you use #XmlElementRef the root element name associated with the class is used to determine the instance to be instantiated. If your example the node entries does not match the #XmlRootElement(name="entry") annotation.
You could try one of the following options (they all work with MOXy's JSON binding, see: http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html):
OPTION 1 - Change #XMLRootElement on Entry
#XmlRootElement(name = "entries")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
OPTION #2 - Change the JSON Document
{"entry":[{"value":"X"},{"value":"Y"},{"value":"Z"}]}}
OPTION #3 - Use #XMLElement instead of #XMLElementRef
If you use the #XmlElement annotation you can specify on the field/property what the node name should be instead of relying on the #XmlRootElement annotation. Also if you annotate the fields you should specify #XmlAccessorType(XmlAccessType.FIELD) at the type level.
import java.util.Set;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "aggregator")
#XmlAccessorType(XmlAccessType.FIELD)
public class Aggregator {
#XmlElement(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
import org.codehaus.jackson.map.annotate.JsonDeserialize;
#JsonDeserialize(as = Entry.class)
public interface IEntry {
String getValue();
}