I have a class with a private constructor that I cannot modify containing many final fields that I wish to serialize with Jackson. Is there any way to force Jackson to serialize all the final fields?
I've tried using a custom filter provider like so: new ObjectMapper().setFilterProvider(new SimpleFilterProvider().addFilter("serialize-final", SimpleBeanPropertyFilter.serializeAll())) but Jackson seems to filter out the final fields before/after it applies my filter.
The use case is my program depends on a library that uses feature flags to indicate which features are enabled. The distributor of the library has compiled the feature flags into final fields on a singleton object that I am able to access at runtime. I wish to add the ability to dump the feature flags when requested to allow for easier debugging and simply serializing them as JSON seems like the easiest solution.
I assume the singleton does not have getters for the final fields (else it works out of the box). You could use the field visibility checker as suggested in this answer:
public class SerializeFinalFields {
#Test
public void doTest() throws JsonProcessingException {
final TestClass t = new TestClass("flag");
final ObjectMapper om = new ObjectMapper();
// Older jackson versions:
// om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// om.setVisibility(
// om.getSerializationConfig().getDefaultVisibilityChecker()
// .withFieldVisibility(JsonAutoDetect.Visibility.ANY));
// newer jackson versions:
om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
final String json = om.writeValueAsString(t);
System.out.println(json);
}
}
class TestClass {
private final String myField;
/* package */ TestClass(final String myField) {
this.myField = myField;
}
}
Related
I've been using a custom typeId resolver for one of my classes, so far I've been leaning on the annotation support:
#JsonTypeInfo(
use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
#JsonTypeIdResolver(ColumnDefinitionTypeResolver.class)
But now I need to customize the creation of the type resolver by passing some other dependencies to it via constructor or setters, and since Jackson is the one who instantiate it I can't find a way around it.
Is there a way to configure the ObjectMapper to use a TypeIdResolver instead of relying on annotations?
Regards
So you have two options:
1) If you're set on using the #JsonTypeIdResolver your stuck using static state in your TypeIdResolver. This probably isn't what you want.
The default JacksonAnnotationIntrospector will try to create an instance of the type you provide using JsonTypeIdResolver per its default constructor. There is currently no way to configure it to do otherwise.
public final class ColumnDefinitionTypeResolver implements TypeIdResolver {
// You could rely on static state.
public static String SOME_ACCESSIBLE_OBJECT = null;
public ColumnDefinitionTypeResolver() {
// This is what gets called.
}
}
ColumnDefinitionTypeResolver.SOME_ACCESSIBLE_OBJECT = "I can affect the implementation from here, but using static state ... be careful";
2) Is create a module to handle deserialization of your type and subtypes.
SimpleModule columnDefinitionModule = new SimpleModule("colDefMod", new Version(1, 0, 0, null))
.addDeserializer(ColumnDefinition.class, new JsonDeserializer() {
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// Need to read the type out and then use ObjectMapper to deserialize using the correct token
}
})
.registerSubtypes(...); // add your subtypes here.
(new ObjectMapper()).registerModule(columnDefinitionModule);
For more detailed examples, see Jackson documentation How-To: Custom Deserializers.
You can also set a custom type id resolver programmatically. Look at the top answer here.
Look for this line:
typeResolver.init(JsonTypeInfo.Id.CLASS, null);
Replace null with your type id resolver.
I'd like to go further on what this question was about, I've been roaming SO for a solid hour now without finding anything.
Basically, what I'm trying to do is having a property properly instanciated through Jackson internal reflection algorithm during deserialization but having this same property not serialized when it comes to serialization.
I know about #JsonIgnore and #JsonIgnoreProperties but apparently I can't seem to use them right : either my property is correctly deserialized when I feed Jackson a proper map of properties but it also appears in the serialized results, either (when using #JsonIgnore) it is not serialized (which is wanted) but also not deserialized (not wanted).
Example :
public class Foo {
/* This is the property I want to be instanciated by Jackson upon deserialization
* but not serialized upon serialization
*/
private final Object bar = null;
public Object getBar() {
return bar;
}
}
To make things worse, as you can see, the property is final (this is why I'm keen on using Jackson reflection ability upon Foo instanciation through deserialization). I've read on potential solution about annotating the setter and the getter differently but I'd like to keep this property final if possible. If not possible, I'd settle for a non-final property.
I would appreciate answers not suggesting custom serializer/deserializer, my code base is currently free of such and if the solution could be of minimal impact, that would be perfect. Again, I'm no Jackson expert so if what I'm asking is not possible I'll obviously accept alternative answers.
I've also read this thread on github but none of the suggested ways of implementation have actually been implemented at the moment.
Thanks
EDIT : to make things clearer
public class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
public void testMethod() throws IOException {
String json = "{\"bar\":\"Value\"}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
System.out.println(foo); // should have a bar property set to "Value"
System.out.println(mapper.writeValueAsString(foo)); // should return an empty JSON object
}
I am not sure whether it is elegant solution but you can use MixIn feature. You have to create new interface which could look like below:
interface FooMixIn {
#JsonIgnore
String getBar();
}
Assume that your POJO looks like this:
class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
Now you have to tell Jackson that you want to ignore this property:
String json = "{\"bar\":\"Value\"}";
System.out.println(json);
ObjectMapper deserializeMapper = new ObjectMapper();
deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println(deserializeMapper.readValue(json, Foo.class));
Above example prints:
{"bar":"Value"}
null
Without deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class); line above program prints:
{"bar":"Value"}
Value
EDIT 1
If you want to achieve result like you showed you have to create two ObjectMappers and customize them. See below example:
String json = "{\"bar\":\"Value\"}";
ObjectMapper deserializerMapper = new ObjectMapper();
Foo foo = deserializerMapper.readValue(json, Foo.class);
System.out.println("Foo object: " + foo);
ObjectMapper serializerMapper = new ObjectMapper();
serializerMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
serializerMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println("JSON: " + serializerMapper.writeValueAsString(foo));
For serialization you have to use one instance and for deserialization you have to use another instance.
Starting with Jackson 2.6, a property can be marked as read- or write-only. It's simpler than hacking the annotations on both accessors (for non-final fields) and keeps all the information in one place. It's important to note that a final field is considered writable by default by Jackson.
However, it's not enough for a final field to allow deserialization, because you can't have a setter on that field: it needs to be set via the constructor, either directly or using a builder or another type that can be deserialized by Jackson. When using the constructor with the properties as parameters, you need to specify which parameter corresponds to which property, using #JsonProperty:
public class Foo {
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private final String bar;
public Foo(#JsonProperty("bar") String bar) {
this.bar = bar;
}
public String getBar() {
return prop;
}
}
Does Jackson have a helper method to return the #JsonProperty annotation value (i.e., the JSON property key) given a bean field name?
Context:
I'm using Jackson to convert client-supplied JSON into a Bean and then using JSR-303 to validate the bean. When validation fails, I need to report a meaningful error message back to the client. The validation objects reference the bean property; the error message should reference the JSON property. Hence the need to map from one to the other.
You can get quite a bit of information via BeanDescription object, although getting one is pretty tricky (mostly since it's designed for Jackson's internal use mostly).
But this is used by a few Jackson extension modules, so it is supported use case. So:
ObjectMapper mapper = ...;
JavaType type = mapper.constructType(PojoType.class); // JavaType to allow for generics
// use SerializationConfig to know setup for serialization, DeserializationConfig for deser
BeanDescription desc = mapper.getSerializationConfig().introspect(type);
you can also safely upcast it to BasicBeanDescription if necessary.
This gives you access to lots of information; either list of logical properties (through which you can find getter/setter/field/ctor-argument that represents it), fully resolved methods (with annotations) and such. So hopefully that is enough.
Logical properties are useful since they contain both external name (one expected from JSON) and internal name derived from getter/setter.
I'm not aware of anything in Jackson to make this particularly easy. A reflections-based solution might suffice.
import java.lang.reflect.Field;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
// {"$food":"Green Eggs and Ham"}
String jsonInput = "{\"$food\":\"Green Eggs and Ham\"}";
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
Bar bar = mapper.readValue(jsonInput, Bar.class);
new Jsr303().validate(bar);
// output:
// I do not like $food=Green Eggs and Ham
}
}
class Bar
{
#JsonProperty("$food")
String food;
}
class Jsr303
{
void validate(Bar bar) throws Exception
{
Field field = Bar.class.getDeclaredField("food");
JsonProperty annotation = field.getAnnotation(JsonProperty.class);
System.out.printf("I do not like %s=%s", annotation.value(), bar.food);
}
}
I'm using Jackson 1.6.4 and Java JDK 6.
I don't want to use Jackson annotations; I want to have immutable Java objects without setters.
The two requirements appear to conflict.
If I add private setters deserialization works fine.
I'm trying to not resort to private setters for my immutable objects - I'm stubborn that way.
I'm in the process of trying a custom implementation of VisibilityChecker to allow ANY field access.
But if anyone has some advice or lessons learned they can share I'd appreciate hearing them.
UPDATE: It's working.
Builder pattern, private constructor - a la Bloch "Effective Java".
It took setting deserialization configuration and visibility, but now it's good to go.
public class JsonMapper
{
private static final int INITIAL_SIZE = 2048;
/** See http://wiki.fasterxml.com/JacksonBestPracticeThreadSafety?highlight=(\bCategoryJackson\b) */
private static ObjectMapper mapper;
static
{
mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
SerializationConfig serializationConfig = mapper.getSerializationConfig();
serializationConfig.setDateFormat(Person.DEFAULT_FORMATTER);
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
deserializationConfig.setDateFormat(Person.DEFAULT_FORMATTER);
deserializationConfig.enable(DeserializationConfig.Feature.AUTO_DETECT_FIELDS);
mapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance().withFieldVisibility(JsonAutoDetect.Visibility.ANY));
}
public static <T> String serialize(T o) throws IOException
{
StringWriter sw = new StringWriter(INITIAL_SIZE);
mapper.writeValue(sw, o);
return sw.toString();
}
public static <T> T deserialize(String source, Class<T> targetClass) throws IOException
{
ByteArrayInputStream stream = new ByteArrayInputStream(source.getBytes());
TreeTraversingParser treeTraversingParser = new TreeTraversingParser(mapper.readTree(stream));
treeTraversingParser.setCodec(mapper);
return treeTraversingParser.readValueAs(targetClass);
}
}
Glad to hear you made it work -- ability to change auto-detection visibility levels is a very powerful feature, but there are so many features that it is not trivial to find all there is.
Couple of additional pointers: if you do not want to add Jackson annotations in POJOs, you can still use mix-in annotations. With this, you can use #JsonCreator to specify non-default constructor to use which allows true immutable value types (more on Jackson and immutable types on this article).
And finally: while builder pattern is not yet directly supported, it has been planned as per this Jira entry.
How do I tell Xstream to serialize only fields which are annotated explicitly and ignore the rest?
I am trying to serialize a hibernate persistent object and all proxy related fields get serialized which I don’t want in my xml.
e.g.
<createdBy class="com..domain.Users " reference="../../values/createdBy"/>
is not something I want in my xml.
Edit: I don’t think I made this question clear. A class may inherit from a base class on which I have no control (as in hibernate’s case) on the base class properties.
public class A {
private String ShouldNotBeSerialized;
}
public class B extends A {
#XStreamAlias("1")
private String ThisShouldbeSerialized;
}
In this case when I serialize class B, the base class field ShouldNotBeSerialized will also get serialized. This is not something I want. In most circumstances I will not have control on class A.
Therefore I want to omit all fields by default and serialize only fields for which I explicitly specify the annotation. I want to avoid what GaryF is doing, where I need to explicitly specify the fields I need to omit.
You can omit fields with the #XstreamOmitField annotation. Straight from the manual:
#XStreamAlias("message")
class RendezvousMessage {
#XStreamOmitField
private int messageType;
#XStreamImplicit(itemFieldName="part")
private List<String> content;
#XStreamConverter(SingleValueCalendarConverter.class)
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
I can take no credit for this answer, just sharing what I have found. You can override the wrapMapper method of the XStream class to achieve what you need.
This link explains in detail: http://pvoss.wordpress.com/2009/01/08/xstream/
Here is the code you need if you don't want the explanation:
// Setup XStream object so that it ignores any undefined tags
XStream xstream = new XStream() {
#Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
#Override
public boolean shouldSerializeMember(Class definedIn,
String fieldName) {
if (definedIn == Object.class) {
return false;
}
return super
.shouldSerializeMember(definedIn, fieldName);
}
};
}
};
You might want to do all your testing before you implement this code because the exceptions thrown by the default XStream object are useful for finding spelling mistakes.
There was already a ticket for the XStream people:
Again, this is by design. XStream is a serialization tool, not a data
binding tool. It is made to serialize Java objects to XML and back. It
will write anything into XML that is necessary to recreate an equal
object graph. The generated XML can be tweaked to some extend by
configuration for convenience, but this is already an add-on. What you
like to do can be done by implementing a custom mapper, but that's a
question for the user's list and cannot be handled here.
http://jira.codehaus.org/browse/XSTR-569
I guess the only direct way is to dive into writing a MapperWrapper and exclude all fields you have not annotated. Sounds like a feature request for XStream.