As of jackson-2.6, required property is only used for #JsonCreator. I have two class A and B. A has a required property and B is inherited from A.
When we use #JsonCreator, we can't get properties information from super class.See the following code, B doesn't check required property 'a'.
If we have many required properties, how to inherit? I don't want to repeat to write #JsonProperty.
public class App {
public static void main(String[] args) {
ObjectMapper objMapper = new ObjectMapper();
String jsonA = "{}"; // miss 'a'
try {
objMapper.readValue(jsonA, A.class);
} catch (IOException e) {
System.out.println("A: Should get exception"); // happen
e.printStackTrace();
}
String jsonB = "{\"b\":\"B\"}"; // miss 'a'
try {
objMapper.readValue(jsonB, B.class);
} catch (IOException e) {
System.out.println("B: Should get exception"); // not happen
e.printStackTrace();
}
}
}
class A {
private String a;
public A() {
}
#JsonCreator
public A(#JsonProperty(value = "a", required = true) String a) {
this.a = a;
}
public String getA() {
return a;
}
}
class B extends A {
private String b;
#JsonCreator
public B(#JsonProperty(value = "b", required = true) String b) {
this.b = b;
}
public String getB() {
return b;
}
}
Constructors are not inherited. in your example, B is calling parameter-less constructor A(). Note when you remove A() from A class, solution will not compile.
In your case, when you need class B to have property a and b you need to have that properties as constructor parameters for B
#JsonCreator
public B(#JsonProperty(value = "a", required = true) String a,
#JsonProperty(value = "b", required = true) String b,
) {
super(a);
this.b = b;
}
You may reuse parametrized constructor A(String a) by calling it from B(String a, String b).
You may also not use constructor and annotate fields a and b as #JsonProperty.
Then it should work without any constructors or any additional code.
Related
I'm struggling to make a mapping from class B to any subclass of A. See code below. It seems it is not possible with ModelMapper, since it ignores converter if it is not exact match. Could you recommend some similar library that is capable of this? Or any recommendation how to do similar behavior, without specifying all possible subclasses explicitly. Thanks a lot.
package com.randakm.p2plibrary.service.main;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.modelmapper.spi.MappingContext;
public class ServiceMain {
/**
* #param args
*/
public static void main(String[] args) {
ModelMapper mapper = new ModelMapper();
mapper.addConverter(new B2AConverter());
B b = new B();
b.b = "some value";
A a = mapper.map(b, AA.class);
System.out.println("a: "+a.a); // I expect this to have the value from b
}
}
abstract class A {
String a;
}
class AA extends A {
String aa;
}
class AAA extends A {
String aaa;
}
class B {
String b;
}
class B2AConverter implements Converter<B, A> {
#Override
public A convert(MappingContext<B, A> context) {
B b = context.getSource();
A a;
try {
a = context.getDestinationType().newInstance();
a.a = b.b;
return a;
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
I have the sama java object TestData in to packages (A & B). I have made a function that processes the object for a standard business functionality.
CommonFunc.java:
import A.TestData ;
class CommonFunc
{
/// .....
public static TestData processTestData(Date d1, String s1){
TestData testData = new TestData ();
/// set some testData porperties based on d1 and s1
/// e.g : testData.setInitialDate(d1);
return testData ;
}
}
The problem here is that the compiler has to load the object from one of the packages lets say package (A), so when I expect the data to be returned to a local variable from package (B) I get incompatible type error :
File using B TestData and needs to call the function processTestData:
import B.TestData;
// ...
TestData obj = CommonFunc.processTestData(new Date(), "test");
// ...
Is there a way to overcome this problem keeping a common function for both?
Is there a way to overcome this problem keeping a common function for both?
No and yes. On the general case, you cannot.
But you can, IFF you can make the two classes adopt the same interface, with the common methods declared in the same interface. See below, with apologies for the change in the class names:
interface C {
public Date getA();
public void setA(Date a);
}
interface C_Factory <X extends C> {
X createInstance();
}
class C1 implements C {
Date a;
int b;
public C1() {
super();
}
public Date getA() { return a; }
public void setA(Date a) { this.a = a; }
public int getB() { return b; }
public void setB(int b) { this.b = b; }
}
class C2 implements C {
Date a;
float b;
public C2() {
super();
}
public Date getA() { return a; }
public void setA(Date a) { this.a = a; }
public float getB() { return b; }
public void setB(float b) { this.b = b; }
}
public class CommonFunc {
// You need this extra param to create instances----
// V
static <X extends C> X doSomething(Date d, Class<X> clazz)
throws InstantiationException, IllegalAccessException
// You'll have to accept those exceptions as well
{
// the next statement uses clazz as a factory for new X instances
// As such, you can abstract the method further and use
// a custom Factory class instead.
X toret=clazz.newInstance();
toret.setA(d);
// something else
return toret;
}
// A custom factory variant of the above
static <X extends C> X doSomething(Date d, C_Factory<X> factory)
{
X toret=factory.createInstance();
toret.setA(d);
// something else
return toret;
}
static public void main(String[] args) {
try {
C1 c1=doSomething(new Date(), C1.class);
C2 c2=doSomething(new Date(), C2.class);
} catch (InstantiationException | IllegalAccessException e) {
// Should not happen
e.printStackTrace();
}
}
}
I do not see how it is possible in the above example you have posted, The best way out is to make the TestData an interface and have implementations in 2 packages. Then, to decide whether to return A TestDataImpl or B TestDataImpl, take another parameter in the processData, for simplicity, let us say a boolean. Based on true or false instantiate A TestDataImpl or B TestDataImpl and return the same. Where the return type of processData is the interface type
This is probably would be the most straightforward way of reusing the processData method.
I'm trying to do a simple example of deserializing json into polymorphic classes. The deserialization fails with the error:
org.codehaus.jackson.map.JsonMappingException: Could not resolve type id 'aField' into a subtype of [simple type, class ...SubClassA]
If I try the deserialization for a single subclass, just using the JSON for that class, it succeeds, but when I put the two classes together inside the SubClassTestObject, it fails. Any ideas for fixing this? Do I need to write a custom deserializer?
Here is my JSON:
{
"classA":{
"aField":"A",
"baseField":"baseA"
},
"classB":{
"baseField":"baseB",
"bField":"B"
}
}
Here are my classes:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = SubClassA.class, name = "classA"),
#JsonSubTypes.Type(value = SubClassB.class, name = "classB")
})
#JsonIgnoreProperties(ignoreUnknown = true)
public abstract class AbstractSimpleClass {
String baseField;
public String getBaseField() {
return baseField;
}
public void setBaseField(String baseField) {
this.baseField = baseField;
}
}
public class SubClassA extends AbstractSimpleClass {
String aField;
public String getaField() {
return aField;
}
public void setaField(String aField) {
this.aField = aField;
}
}
public class SubClassB extends AbstractSimpleClass {
String bField;
public String getbField() {
return bField;
}
public void setbField(String bField) {
this.bField = bField;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class SubClassTestObject {
#JsonProperty("classA")
SubClassA a;
#JsonProperty("classB")
SubClassB b;
public SubClassA getA() {
return a;
}
public void setA(SubClassA a) {
this.a = a;
}
public SubClassB getB() {
return b;
}
public void setB(SubClassB b) {
this.b = b;
}
}
And here is my test:
#Test
public void testBoth() throws IOException, URISyntaxException {
ClassLoader classLoader = getClass().getClassLoader();
json = new String(Files.readAllBytes(Paths.get(classLoader.getResource("test/so-example.json").toURI())));
ObjectMapper mapper = new ObjectMapper();
mapper.registerSubtypes(SubClassA.class, SubClassB.class);
SubClassTestObject testObj = mapper.readValue(json, SubClassTestObject.class); //Fails here
SubClassA a = testObj.getA();
SubClassB b = testObj.getB();
assertTrue(a.getBaseField().equals("baseA"));
assertTrue(b.getBaseField().equals("baseB"));
assertTrue(a.getaField().equals("A"));
assertTrue(b.getbField().equals("B"));
}
After your edit:
This
#JsonProperty("classA")
SubClassA a;
is completely unrelated to
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
The JsonTypeInfo.As.WRAPPER_OBJECT is meant to be implicit. It's not something you map in your POJOs. You won't be able to map your current JSON with this strategy. Either do what's below or change your JsonTypeInfo to use a JsonTypeInfo.As.PROPERTY for example, then provide the corresponding #type (and its appropriate value) property in the JSON.
Pre-edit:
Your SubClassTestObject class has two properties, a and b, because of
public SubClassA getA() {
return a;
}
public SubClassB getB() {
return b;
}
These aren't present in your JSON. And since you told Jackson to ignore unknown properties, it doesn't fail to deserialize. However, both of them are going to be left uninitialized.
The JSON you meant to deserialize is
{
"a": {
"classA": {
"aField": "A",
"baseField": "baseA"
}
},
"b": {
"classB": {
"baseField": "baseB",
"bField": "B"
}
}
}
which has a and b for your root SubClassTestObject object. And those use wrapper objects with the appropriate JsonTypeInfo name.
I have:
class A
{
public String getID() { return "id of A";}
}
class B extends A
{
public String getID() { return "id of B"; }
}
and
class C {
public A returnA() {
return new A();
}
}
Now I somehow need to do:
C c = new C();
B b = (B)c.returnA();
String id = b.getId();
But I don't have access to implementation of C.returnA(), and I can't change return type of it to B.
You are casting a parent into a children.
You can never do that, because new A() is absolutely not a B.
Consider this: String extends Object. Now try to cast (String) new Object(). It wouldn't make any sense at all.
Because your object is not a B anyway, there is no way it could have the behavior of B.
What you want here is use a Decorator Pattern. See http://en.wikipedia.org/wiki/Decorator_pattern
Here is an example of what a implementation of a Decorator could be:
public class B extends A {
private A decorated;
public B(A decorated) {
this.decorated = decorated;
}
#Override
public String getID() {
return "id of B";
}
#Override
public void otherMethodOfA() {
return decorated.otherMethodOfA();
}
}
Note that it is mandatory to override all methods of A to make sure you call the method on the decorated element. (here otherMethodOfA is an example)
Use like this:
C c = new C();
B b = new B(c.returnA());
String id = b.getID();
That won't work. c.returnA() returns an A. An A is not a B. (A B is an A, but that's not relevant here).
The answer of njzk2 is perfect. Anyway, if you ended up reading this post and like me, you don't like overriding every method, you can just do this:
public class B extends A {
public B(A nonDecorated) {
this.anotherValueOfA = nonDecorated.getAnotherValueOfA();
}
#Override
public String getID() {
return "id of B";
}
}
There is no need to override every method and the object is constructed with the values from its parent.
This is, of course, assuming class A is:
class A {
private int anotherValueOfA;
public String getID() {return "id of A";}
public int getAnotherValueOfA() {return this.anotherValueOfA;}
}
How do you initialize this:
class A {
final B b;
A(B b) {
this.b = b;
}
}
class B {
final A a;
B(A a) {
this.a = a;
}
}
DI framework, reflection, better design?
Motivation and a use case (added):
My particular use case is simplifying field access in A's and B's sub-classes. So I'm injecting them to shortly reference them by fields in the derived classes without a need to declare explicitly in each sub-class.
There is also a recommendation on DI that objects should better be immutable: Guice best practices and anti-patterns.
You could use a factory method
class A {
final B b;
A(B b) {
this.b = b;
}
}
abstract class B {
final A a;
B() {
this.a = constructA();
}
protected abstract A constructA();
}
public class C {
public static void main(String []args){
new B(){
protected A constructA(){
return new A(this);
}
};
}
}
Though it may look dirty, but I prefer to replace one of the final references with Supplier (like one in Guava or Java 8) like:
class A {
final Supplier<B> b;
A(Supplier<B> b) {
this.b = b;
}
// keeping this constructor just for usability's sake
A(B b) {
this.b = ofInstance(b); // using Guava's Suppliers.ofInstance here
}
}
class B {
final A a;
B(A a) {
this.a = a;
}
}
public static void main(String[] args) {
// using MutableSupplier.create() static factory method
MutableSupplier<B> bRef = create();
A a = new A(bRef);
B b = bRef.set(new B(a));
}
where MutableSupplier looks somehow like the following:
import com.google.common.base.Supplier;
public class MutableSupplier<T> implements Supplier<T> {
private boolean valueWasSet;
private T value;
private MutableSupplier() {
}
#Override
public T get() {
if (!valueWasSet) {
throw new NullPointerException("Value has not been set yet");
}
return value;
}
public T set(final T value) {
if (valueWasSet) {
throw new IllegalStateException("Value has already been set and should not be reset");
}
this.value = value;
this.valueWasSet = true;
return value;
}
public static <T> MutableSupplier<T> create() {
return new MutableSupplier<T>();
}
}
I know that MutableSupplier's mutability looks super-ugly for immutability enthusiasts but I found that using it is more or less acceptable in such cases :)
What you are having is a circular dependency. The only way I can think of is to not declare the fields as final and have your dependency injected using setter injection instead of constructor injection.
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);