Is it possible for Jaxb to unmarshall to an interface? - java

my current code marshalls perfectly, and I get the element I want inside of my resulting XML. i.e. <food>Beef</food>
However, the problem comes when I have to unmarshall this back to a java object. Everything returns fine except the food variable. I originally did not have the XmlElement(required = true) on top, and the food element would always unmarshal back to null. Then, I added the required=true section and I am getting issues with the interface. I did some digging and from what I can gather, jaxb can't really unmarshal into an interface since it doesn't know the concrete type to marshall into.
Current error if this helps:
Can not set FoodInterface field BigPayload.food to
com.sun.org.apache.xerces.internal.dom.ElementNSImpl
My Java classes are as follows:
#XmlSeeAlso({MeatFoods.class, VeggieFoods.class})
#XmlType(name ="BigPayload", propOrder = //stuff goes here
#XmlRootElement(name = foodPayload)
public class BigPayload implements Payload{
#XmlElements({#XmlElement(type = MeatFoods.class),
#XmlElement(type = VeggieFoods.class),
#XmlElement(required = true)})
protected FoodInterface food;
protected Grade grade;
//grade/food setters and getters
}
#XmlTransient //If this isn't here, I get the jaxB cannot handle interfaces and no default constructor error
public interface FoodInterface{ //stuff here}
#XmlType(name = "MeatFoods")
#XmlEnum
public enum MeatFoods implements FoodInterface{
Chicken(1, Chicken)
Beef(2, Beef)
Pork(3, Pork)
int value;
String name;
#Override
public int getValue()
#Override
public String getName()
public static FoodInterface getEnumFromValue(int value){//gets stuff}
public static FoodInterface getEnumFromName(String name){//gets stuff}
}
I just wanted to know if that is correct, and there's no real good way to unmarshall an interface type. Is this true? I saw a lot of other questions were about marshalling interfaces, and the unmarshalling questions did not really get answers to my satisfaction. Any answer is appreciated, and I know this isn't a minimal reproducible example, but I'm more looking for a verbal answer instead of a code fix or anything. Although, if there's anything blatantly wrong in the code please let me know!

For the standard cases JAXB can only use (abstract) classes not interfaces.
Options that i can think of
You can use interfaces with #XmlAdapter. See example: [1]
Use Object for JAXB Bindings and expose the interface with casting. (Maybe add validation logic into the `afterUnmarshal(Unmarshaller u, Object parent). [2]
Bind a private field to #XmlAnyElement and do some further processing in afterUnmarshal(Unmarshaller, Object), add #XmlTransient to the target. See example: [3]
With some creativity there might be some other options. But i think all boil down to bascially: try to get to the "raw" parsing options and fill the interface reference manually.
[1]
public static interface Food {
String name();
}
public enum Veggie implements Food {
SALAD;
}
public static enum Meat implements Food {
CHICKEN;
}
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement
public static class UseInterface {
#XmlJavaTypeAdapter(FoodAdapter.class)
#XmlAttribute
private Food food;
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
}
public static class FoodAdapter extends XmlAdapter<String, Food> {
#Override
public Food unmarshal(String v) throws Exception {
try {
return Veggie.valueOf(v);
} catch (IllegalArgumentException e) {
}
try {
return Meat.valueOf(v);
} catch (IllegalArgumentException e) {
}
throw new IllegalArgumentException("Unknown Food:" + v);
}
#Override
public String marshal(Food v) throws Exception {
return v.name();
}
}
[2]
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement
public static class UseInterface {
#XmlElement
private Object food;
public Food getFood() {
return (Food) food;
}
public void setFood(Food food) {
this.food = food;
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
if (food != null && !(food instanceof Food)) {
throw new IllegalArgumentException("food is of wrong type: " + food.getClass().getName());
}
}
}
JAXBContext newInstance = JAXBContext.newInstance(UseInterface.class, Meat.class, Veggie.class);
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><useInterface><food xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"meat\">CHICKEN</food></useInterface>";
newInstance.createUnmarshaller().unmarshal(new StringReader(xml));
[3]
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement
public static class UseInterface {
#XmlAnyElement
private org.w3c.dom.Element foo;
#XmlTransient
private SomeInterface ifc
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
NamedNodeMap attributes = foo.getAttributes();
// do something with foo on DOM level to bind the subtree to an interface manually
}
}

Related

Using class as extend of the Entity, but it is not the physical table in the model

I am not sure if title of the post is understandable, but I will try to explain it with examples.
The main class is the entity, lets name it Animal
#Entity
public class Animal {
#Id
private Long id;
private String param1;
}
The param1 is some basic property which only extended classes will expose what it represents:
public class Cat extends Animal {
public String getName() {
return super.getParam1();
}
public void setName(String name) {
super.setParam1(name);
}
}
public class Dog extends Animal {
public String getBreed() {
return super.getParam1();
}
public void setBreed(String breed) {
super.setParam1(breed);
}
}
When creating a new object of Cat and trying to save it, I get the error Unknown entity: com.example.Cat.
I have read about persistence inheritance but I think this is not the case since mine extended classes are just some logic in the services and not the actual tables in the model.
Is there any solution on how I can save the superclass ?

Java generics override static methods workaround

I'm working on a project that requires me to serialize and deserialize generic objects. The way I'm going about this, is defining an abstract class Serializer that implements a toBytes() and a static fromBytes(). All is well with this approach, as I can pass an object instance to a generic class Foo that expects a Serializer subclass, and I can ensure the object knows how to serialize and deserialize itself.
Now my question. Java serialization kinda sucks. I have multiple implementations I'd like to try swapping in and out, and ultimately I'd like the user to be able to decide the format. How would I go about changing the implementation details of Serializer? I know I can't override static methods, so how would I do this without decoupling Foo and Serializer and not being able to ensure my generic object has the appropriate toBytes() and fromBytes() method in Foo?
Here is code if anyone is confused:
public abstract class Serializer {
public static Serializer fromBytes(byte[] bytes) {
...
}
public byte[] toBytes() {
...
}
}
public class Foo<T extends Serializer> {
private T t;
public Foo(T t) {
this.t = t;
}
public void foo() {
t.toBytes(); //this will polymorph into the correct call because it's called on the object instance and not the Serializer class
}
public void bar(byte[] bytes) {
T.fromBytes(bytes); // I'd like to be able to override this method so I can use different implementations
}
}
I'm not sure if this is a good approach, but how about using Jackson library and serialize your object as a json node? for example:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = SoundFile.class, name = "sound"),
#Type(value = VideoFile.class, name = "video")
})
abstract class File{
private String id;
private String type;
#JsonCreator
public File(#JsonProperty("id") String id)
{
this.id=id;
}
public String getId() {return this.id;}
public abstract String getType();
}
class SoundFile extends File{
#JsonCreator
public SoundFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "sound";
}
}
class VideoFile extends File{
#JsonCreator
public VideoFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "video";
}
}
public class GenericApp {
public static void main(String[] args) {
ObjectMapper om = new ObjectMapper();
List<File> files = Arrays.asList(new VideoFile("1"),new SoundFile("2"));
//serialize
List<byte[]> fileSerialized = files.stream().map(file->{
try {
return om.writeValueAsBytes(file);
}catch(Exception e) {return null;}
}).collect(Collectors.toList());
//de-serialize
List<File> filesDeSerialized = fileSerialized.stream().map(bytes ->{
try {
return om.readValue(bytes, File.class);
}
catch(Exception e) {return null;}
}).collect(Collectors.toList());
filesDeSerialized.stream().forEach(file->{
System.out.println("id :"+file.getId()+" - "+file.getClass());
});
}
}
this would properly deserialize these objects and print:
id :1 - class com.dsncode.stackoverflow.VideoFile
id :2 - class com.dsncode.stackoverflow.SoundFile
however, you should define a #JsonTypeInfo and a #JsonSubType for all your sub-classes of your Generic Type. Because, by indicating this field, you will indicate to Jackson deserializer, which class should create for your generic type.

JAXB using #XmlJavaTypeAdapter to marshal a subset of a Collection

I've got an entity which contains a collection of a different type of entities. What I want to do is have JAXB marshal only a select subset of the collection, based on some criteria.
#XmlRootElement
#Entity
public class A{
// other fields
#OneToMany(mappedBy = "x", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<B> bees;
#XmlJavaTypeAdapter(BFormatter.class)
public Collection<B> getBees() {
return bees;
}
public void setBees(Collection<B> bees) {
this.bees= bees;
}
}
#XmlRootElement
#Entity
public class B{
// fields
}
public class BFormatter extends XmlAdapter<Collection<B>, Collection<B>>{
#Override
public Collection<B> unmarshal(Collection<B> v) throws Exception {
return v;
}
#Override
public Collection<B> marshal(Collection<B> v) throws Exception {
Collection<B> subset;
// making subset
return subset;
}
}
This results in errors saying "java.util.Collection is an interface, and JAXB can't handle interfaces" and that "java.util.Collection does not have a no-arg default constructor."
What am I doing wrong, and is this even the right way to go about it?
The important thing is that you can't adapt a Collection (an interface) to something JAXB can handle, since it doesn't marshal an ArrayList or some other collection class. It is designed to marshal (bean) classes containing fields that are Lists or similar, which is meant to "disappear", remaining as the mere repetition of its elements. In other words, there's no XML element representing the ArrayList (or whatever) itself.
Therefore, the adapter has to modify the containing element. (See below for alternatives.) The following classes are working; just assemble a Root element and modify the AFormatter according to your design. (The comments refer to the example at
https://jaxb.java.net/tutorial/section_6_2_9-Type-Adapters-XmlJavaTypeAdapter.html#Type%20Adapters:%20XmlJavaTypeAdapter.)
(Most classes should be modified to avoid making fields public, but as it is, it is brief and working.)
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root{ // Training
#XmlElement
private A a; // Brochure
public Root(){}
public A getA(){ return a; }
public void setA( A value ){ a = value; }
}
#XmlJavaTypeAdapter(AFormatter.class)
public class A{ // Brochure
private Collection<B> bees;
public A(){
bees = new ArrayList<>();
}
public Collection<B> getBees() {
if( bees == null ) bees = new ArrayList<>();
return bees;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class B{ // Course
#XmlElement
private String id;
public B(){}
public String getId(){ return id; }
public void setId( String value ){ id = value; }
}
public class AFormatter extends XmlAdapter<BeeHive, A>{
#Override
public A unmarshal(BeeHive v) throws Exception {
A a = new A();
for( B b: v.beeList ){
a.getBees().add( b );
}
return a;
}
#Override
public BeeHive marshal(A v) throws Exception {
BeeHive beeHive = new BeeHive();
for( B b: v.getBees() ){
if( b.getId().startsWith("a") ) beeHive.beeList.add( b );
}
return beeHive;
}
}
public class BeeHive { // Courses
#XmlElement(name="b")
public List<B> beeList = new ArrayList<B>();
}
Alternatives: It would be quite simple if the regular getter of the B-list would return the ones that should be marshalled. If the application needs to see all, an alternative getter could be added. Or, the class could have a static flag that instructs the getter to return a List to be used for marshalling, or the regular list at other times.

How to deserialize JSON Array contained an abstract class without modifying a parent class?

I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}

How to make JAXB Marshaller marshal abstract class based on its runtime type?

Consider the following abstract class -
public abstract class Car
{
public abstract void drive(double miles);
}
Here's a sample class (for illustration purposes) that extends the above class.
public class Ferrari extends Car
{
private String lastUsed; // Ferrari specific field not in Car
private boolean f1Car; // Ferrari specific field not in Car
#XmlElement
public void setF1Car(boolean f1Car)
{
this.f1Car = f1Car;
}
public boolean isF1Car() { return f1Car; }
#XmlElement
public void setLastUsed(String lastUsed)
{
this.lastUsed = lastUsed;
}
public String getLastUsed() { return lastUsed; }
public void drive(double miles)
{
// implementation
}
}
I have a report class that contains a Car object -
#XmlRootElement
public class CarTestReport
{
private String date;
private double miles;
private Car car;
#XmlElement
public void setDate(String date) { this.date = date;}
public String getDate() {return date;}
#XmlElement
public void setMiles(double miles) { this.miles = miles; }
public double getMiles() {return miles;}
#XmlElement
public void setCar(Car car) { this.car = car; }
public Car getCar() { return car; }
}
And here is the piece of code using JAXB to Marshall a CarTestReport object -
public static void main(String[] args) throws Exception
{
Ferrari ferrari = new Ferrari();
ferrari.setLastUsed("July 5 2012");
ferrari.setF1Car(false);
CarTestReport report = new CarTestReport();
report.setDate("July 6 2012");
report.setMiles(200);
report.setCar(ferrari);
File file = new File("carTestReport.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(report, file);
}
The problem is, because of the abstract type Car, JAXB ignores it and doesn't marshall the Ferrari object when it marshals the CarTestReport object. The output I get is this -
<carTestReport>
<car/>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
As you can see, nothing was entered under the 'car' node, even though the Ferrari object was populated. How to solve this problem?
The JAXB system doesn't look through the classpath for any possible JAXB-annotated classes. You have to help it find them. In your sample code, it simply doesn't know about the existence of the Ferrari class. (It only sees Car because that's the return type of the getter in CarTestReport.)
One quick and dirty way to tell JAXB about Ferrari is to add #XmlSeeAlso({Ferrari.class}) at the top of your Car class. Then you'll get output like this:
<carTestReport>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ferrari">
<f1Car>false</f1Car>
<lastUsed>July 5 2012</lastUsed>
</car>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
Another way to tell JAXB about Ferrari would be to pass that class to the JAXBContext.newInstance method, i.e.:
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class,
Ferrari.class);
Or if all of your JAXB classes are in the same package, e.g. com.mycompany.carstuff, then you could do this:
JAXBContext jaxbContext = JAXBContext.newInstance("com.mycompany.carstuff");
And in this last case it WILL search for all classes in that package.
If you want it to emit an element named ferrari (instead of the <car xsi:type="ferrari"> like above), one possibility is to add #XmlType to the top of your Car class, like this:
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
#XmlSeeAlso({Ferrari.class})
#XmlType
public abstract class Car {
public abstract void drive(double miles);
}
...and put #XmlRootElement on Ferrari, e.g.:
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Ferrari extends Car {
// ...
}
From what I understand, this combination of annotations tells JAXB that the Car class maps to an XML schema type (so you won't get any elements named "car"), and that the Ferrari class is an element of that type (so you can have elements named "ferrari"). And the "root" in #XmlRootElement is misleading... it can be an element anywhere in the structure of your objects.
You need to annotate your Ferrari class with #XmlRootElement and replace #XmlElement on the car attribute with #XmlElementRef

Categories