Details
-
Type:
Improvement
-
Status:
Closed
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: 1.4.2
-
Fix Version/s: 1.5
-
Component/s: JSON tree (JsonNode)
-
Labels:None
-
Environment:Jackson 1.4.2
-
Number of attachments :
Description
Hello Jackson team !
First, thank you for creating this fast Json processing framework. I recently switched from Gson to Jackson on an Android project and the results outperform my expectations (Gson creates tons of temporary objects, and Android GC sucks).
One thing I enjoy with Gson is to be able to serialize/deserialize entities without no fwk specific configuration. I can create pojos with private fields + getters, a private constructor and no setters, and Gson is able to deserialize my entities from Json.
Jackson however requires the fields to either be public, have a specific annotation (@JsonProperty), or have the corresponding setter.
Just to be clear : I'd like to be able to serialize / deserialize such a class :
public class MyBean {
private String name;
public String getName() {
return name;
}
}
It would be nice if there was an option, disabled as default, to allow private fields with no annotation to be auto detected. Something like (De)SerializationConfig.Feature.AUTO_DETECT_PRIVATE_FIELDS
I only looked at Deserialization for now since it's what I need. I think the main changes would occur in BasicBeanDescription._findPropertyFields (l 762) :
if (!autodetect || !af.isPublic()) {
continue;
}
=> would become something like
if (!autodetect || (!af.isPublic() && !autodetect_private_fields)) { continue; } }
This feature would be nice for prototyping, and quickly being able to parse Json. It would also help migrating from Gson.
I managed to enable this feature when deserializing Json with Jackson 1.4.2, but the code is dirty... here is what I did anyway :
- Create a subclass of BasicBeanDescription that overrides _findPropertyFields to remove the isPublic check.
- Create a subclass of BasicClassIntrospector that overrides forDeserialization method and return my custom BasicBeanDescription instead of a standard BasicBeanDescription.
- Create an instance of DeserializationConfig injected with the custom BasicClassIntrospector, and inject it in the ObjectMapper.
Here is the ugly code :
ObjectMapper om = new ObjectMapper();
BasicClassIntrospector bci = new BasicClassIntrospector() {
@Override
public BasicBeanDescription forDeserialization(DeserializationConfig cfg, JavaType type,
org.codehaus.jackson.map.ClassIntrospector.MixInResolver r) {
final AnnotationIntrospector ai = cfg.getAnnotationIntrospector();
final AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(), ai, r);
// everything needed for deserialization, including ignored
// methods
ac.resolveMemberMethods(getDeserializationMethodFilter(cfg), true);
// include all kinds of creator methods:
ac.resolveCreators(true);
// yes, we need info on ignored fields as well
ac.resolveFields(true);
return new BasicBeanDescription(type, ac, ai) {
@Override
public LinkedHashMap<String, AnnotatedField> _findPropertyFields(boolean autodetect,
Collection<String> ignoredProperties, boolean forSerialization) {
Boolean classAD = ai.findFieldAutoDetection(ac);
if (classAD != null) {
autodetect = classAD.booleanValue();
}
LinkedHashMap<String, AnnotatedField> results = new LinkedHashMap<String, AnnotatedField>();
for (AnnotatedField af : ac.fields()) {
/*
- note: some prefiltering has been; no static or
- transient fields included; nor anything marked as
- ignorable (@JsonIgnore)
*/
/*
- So far so good: final check, then; has to either
- (a) be marked with JsonProperty (or
- JsonSerialize) OR (b) be public
*/
String propName = forSerialization ? ai.findSerializablePropertyName(af) : ai
.findDeserializablePropertyName(af);
if (propName != null)Unknown macro: { if (propName.length() == 0) { propName = af.getName(); } }elseUnknown macro: { // nope, but is a public field? // ****** MY CHANGE }
}
/*
- Yup, it is a valid name. But do we have a
- conflict? Shouldn't usually happen, but it is
- possible... and for now let's consider it a
- problem
*/
AnnotatedField old = results.put(propName, af);
if (old != null) { String oldDesc = old.getFullName(); String newDesc = af.getFullName(); throw new IllegalArgumentException("Multiple fields representing property \"" + propName + "\": " + oldDesc + " vs " + newDesc); }
}
return results;
}
};
}
};
DeserializationConfig deserConfig = new DeserializationConfig(bci, new JacksonAnnotationIntrospector());
om.setDeserializationConfig(deserConfig);
om.configure(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, true);
om.configure(DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
If you find any cleaner way, please let me know !
Thank you for suggestion! (and sample code)
I am glad Jackson was worked well so far, aside from auto-detection. Performance has been an important design criteria, and part of that has been to try to keep processing streamline (including avoiding unnecessary garbage object creation).
This issue (only being able to auto-detect public fields) has been on our radar, but no Jira entry had been added.
My initial thought was to add visibility setting, to allow defining visibility level a field should have to qualify as property. Current default would then be public, but could be changed to private/protected (and maybe package, and 'none').
As an added bonus, maybe there could also be additional property for @JsonAutoDetect, to allow for per-class override (mostly useful when used via annotation mix-ins).
Would this work? I think this could be added for 1.6 (1.5 release is close and feature set is frozen). It really has more to do with how to configure it, like you say, implementation should be easy.
The only other thing is that there are plans to rewrite property-discovery and annotation-introspection pieces, so this could naturally fit within that work.