Jackson: final field written to after it is set by #JsonCreator? - java

It seems Jackson uses reflection to write additional attributes directly into fields even if a #JsonCreator constructor was used and the field is marked as final.
Example:
public class Test {
static class X {
final String s;
#JsonCreator
public X(#JsonProperty("a") String a) {
s = "s";
}
public String getS() {
return s;
}
}
#org.junit.Test
public void ds() throws Exception {
ObjectMapper om = new ObjectMapper();
X x = om.readValue("{`a`:``, `s`: `t`}".replace('`', '"'), X.class);
assertEquals("s", x.s);
}
}
The assert will fail with org.junit.ComparisonFailure: expected:<[s]> but was:<[t]>.
Is this behavior documented anywhere? Is there anyway to disable this globally?
Also, I think this is a very dangerous design: if there are some value that should be read-only to the client, this effectively allows the client to set them even if the class is well designed according to normal immutable class guidelines.

First: yes, Jackson allows deserialization of all visible and not just those for which #JsonCreator property exists. So it is possible to set a smaller set of properties via constructor, and others via setters or fields. This may be needed for some cases like cyclic types.
As to how to prevent use of s for deserialization here. An obvious way is to add #JsonIgnore for field, although if so you will also need #JsonProperty for getS() to avoid both being removed.
But there are other settings as well, in MapperFeature.
ALLOW_FINAL_FIELDS_AS_MUTATORS: if you disable this, final fields are never used directly for deserialization
INFER_PROPERTY_MUTATORS: if you disable this, then neither fields nor setters are "pulled in" in cases where they are not otherwise visible (for fields, public is needed to be visible; for setters, even private is fine)
So may want to disable one or both settings.

Related

Use lombok to generate constructor that param takes in additional params

I am refactoring some legacy code, trying to use Lombok to make it a bit cleaner.
Right now the constructor of my class is defined as follows:
public class MyClass {
private final YourClass yourClass;
public MyClass(final A a, final B b) {
yourClass = new YourClass(a, b);
}
}
How do I use Lombok to make this happen?
If you have additional parameters of an arbitrary type, there is no way for Lombok (or any other annotation processing mechanism) to determine how you want your constructor to look like and behave.
You will have to define your own constructor or a static factory method. Or use the builder pattern.

Mock final private variable using Mockito/Powermock framework

I'm trying to mock a class that looks like below
public class MessageContainer {
private final MessageChain[] messages;
MessageContainer(final int numOfMessages, final MessageManagerImpl manager, final Object someOtherStuff) {
messages = new MessageChain[numOfMessages]
// do other stuff
}
public void foo(final int index) {
// do something
messages[index] = getActiveMessage();
}
}
My test code would be as followed:
#Test
public void testFoo() {
MessageContainer messageContainer = Mockito.mock(MessageContainer.class);
Mockito.doCallRealMethod().when(messageContainer).foo(anyIndex);
}
I got a NullPointerException since 'messages' is null. I tried to inject the mock by using #InjectMocks, however this case is not supported since not every parameters of the constructor are declared as members.
I also tried to set the 'messages' field by using WhiteBox
Whitebox.setInternalState(messageContainer, MessageChain[].class, PowerMockito.mock(MessageChain[].class));
but I got a compile error since setInternalState only supports (Object, Object, Object) and not Object[].
Is there any possible way to mock a private final field?
Thank you guys in advance.
Based on your edits and comments, I would say mocking this class and verifying the method was invoked is sufficient.
If it is third-party code, you should rely only on its method signature, which comprises the class's public API. Otherwise you are coupling your tests too tightly to something you have no control over. What do you do when they decide to use a Collection instead of an array?
Simply write:
MessageContainer container = mock(MessageContainer.class);
//Your code here...
verify(container).foo(obj);

Java annotation to set field to a static instance?

I've been playing with annotations, and I'm wondering how to go about doing this. What I'd like to do is to be able to have a field declared in a class and annotated such that the field will be initialized with a static instance of the class.
Given an annotation like this:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME) //or would this be RetentionPolicy.CLASS?
public #interface SetThisField {
}
Something like this:
public class Foo {
#SetThisField
private Bar bar;
}
I've played around with using a parser and setting this at runtime, which works but isn't as elegant as I'd like.
I can't find any really good examples of RetentionPolicy.CLASS but the documentation seems to indicate that I could somehow make the declaration of "bar" get compiled into this:
private Bar bar = Bar.getInstance();
It wouldn't look that way in the source code of course, but it would in the byte code and it would behave like that at runtime.
So am I off base here? Is this possible? Or is the parser the way to go with it?
UPDATE: This is the guts of the parser I'm using
public static void parse(Object instance) throws Exception {
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
//"Property" annotated fields get set to an application.properties value
//using the value of the annotation as the key into the properties
if (field.isAnnotationPresent(Property.class)) {
Property property = field.getAnnotation(Property.class);
String value = property.value();
if (!"".equals(value)) {
setFieldValue(instance, field, properties.getProperty(value));
}
}
//"Resource" annotated fields get static instances of the class allocated
//based upon the type of the field.
if (field.isAnnotationPresent(Resource.class)) {
String name = field.getType().getName();
setFieldValue(instance, field, MyApplication.getResources().get(name));
}
}
}
private static void setFieldValue(Object instance, Field field, Object value) throws IllegalAccessException {
boolean accessibleState = field.isAccessible();
field.setAccessible(true);
field.set(instance, value);
field.setAccessible(accessibleState);
}
I would suggest doing the replacement at run time. This is much simpler to implement and test. Changing the byte code at build time is relatively error prone and tricky to get right. For example you would need to understand how byte code is structured and in this case how to add the code to all the constructors in the right place in the code.
If you make the retention RUNTIME, you can have a library which examines the annotation and sets the value after the object is created.

Use #JacksonInject with #JsonCreator on a top level map

With Jackson json library, it is possible to deserialize object through the use of the #JsonCreator, and be given the "top level" map representing the input json, as follows:
class MyClass {
final int field;
#JsonCreator
public MyClass(Map<String, Object> map) {
this.field = (int) map.get("theInt");
}
}
or even on a static factory method:
class MyClass {
final int field;
public MyClass(int theInt) {
this.field = theInt;
}
#JsonCreator
static MyClass create(Map<String, Object> map) {
return new MyClass((int) map.get("theInt"));
}
}
The previous examples can process the following kind of json input:
{
"key1":"value1",
"key2":"value2",
"key3":"value3"
}
This is particularly useful in my case because I would like to deserialize a json which structure I don't know. Being given access to what I call the "top level map" makes things simple.
I would like to deserialize my objects this way as it also allows to create immutable objects, instead of using #JsonAnySetter which doesn't permit this, and #JsonProperty which I can't use as I don't know the properties name in advance as I mentioned earlier.
Then to go further, I would like to inject some configuration in my factory method, and Jackson allows this through the use of #JacksonInject and a call to withInjectableValues(InjectableValues) on the ObjectMapper.
This is eventually the kind of code I would like to use:
class MyClass {
final MyField[] myFields;
public MyClass(MyField... myFields) {
this.myFields = myFields;
}
#JsonCreator
static MyClass create(#JacksonInject("conf") Conf conf, Map<String, Object> map) {
MyFields[] myFields;
// initialize myFields (or any other object) based on the content of map
// and also make use of the inject conf
return new MyClass(myFields);
}
}
Unfortunately, Jackson throws the following kind of exceptions:
when trying the trick on the constructor
JsonMappingException: Argument #1 of constructor [constructor for MyClass, annotations: {JsonCreator=#JsonCreator()}] has no property name annotation; must have name when multiple-paramater constructor annotated as Creator
when trying the trick on the factory method
JsonMappingException: Argument #1 of factory method [method create, annotations: {JsonCreator=#JsonCreator()}] has no property name annotation; must have when multiple-paramater static method annotated as Creator
Does anyone know how I could solve the problem?
To sum up the requirements, I need:
access to the top level map (don't know the json property names in advance)
to create an immutable object (so can't use #JsonAnySetter)
to inject some conf to the #JsonCreator decorated constructor or factory method
I cannot change the json input format, which looks like this:
{
"key1":"value1",
"key2":"value2",
"key3":"value3"
}
[EDIT]
This is a known issue: http://jira.codehaus.org/browse/JACKSON-711 (not fixed yet)
Right, you would like to both use "delegating" creator (single argument, into which JSON input is first bound) -- different from "property-based" creator where set of named parameters are passed -- and injectable value(s). This should ideally work, but I think it might not work currently.
I think there is a Jira entered for this, so you can check it (http://jira.codehaus.org/browse/JACKSON) out.
Just to make sure: are you using version 1.9.2? There have been some fixes in this are since 1.9.0; which at least would give better error message.

Method collision between JAXB and rest of the Application

I am converting an existing POJO to be an JAXB compliant. Everything works fine except for one of the getter method of the pojo where I need an additional logic specific to rendering XML. However, this getter is already called somewhere within the application and I cannot modify this method's behavior.
How do I normally deal with such method name collisions? Is there a way I create a separate method just for JAXB purpose adn mark the currentmethod as XMlTransient?
Thanks
Yes, exactly what you said would work. Make the one method #XmlTransient, then write another method and make it an #XmlElement(name="whatever element name").
You can put XmlAccessorType(XmlAccessType.FIELD) on the class. Then the JAXB annotations will be picked up from the field names, and not the method names. For example:
#XmlAccessorType(XmlAccessType.FIELD)
public class MyType {
#XmlElement String f1;
#XmlElement Integer f2;
// JAXB doesn't care about these:
public String getF1() {return f1;}
public String getF2() {return f2;}
public void setF1(String f1) {this.f1 = f1;}
public void setF2(Integer f2) {this.f2 = f2;}
}

Categories

Resources