Changing names of properties while Serializing to JSON without source code - java

Need to serialize java objects to JSON while doing compression such as name change, exclusion etc. Objects use class from jar, source code of which is not available.
Looked through many libraries(Jackson , Gson), but found none solving this particular problem. Most of them are annotations based, which I can't use given I don't have source code.
One way to solve this problems is, use reflection and recursively go through object until you find a property name of which should be replaced or object is excluded in serialized JSON.
Need solution for this. Better if it is already implemented and tested.

You can also have a look at Genson library http://code.google.com/p/genson/.
You can rename and filter with quite concise code:
// renames all "fieldOfName" to "toName", excludes from serialization
// and deserialization fields named "fieldNamed" and declared in DefinedInClass
// and uses fields with all visibility (protected, private, etc)
Genson genson = new Genson.Builder().rename("fieldOfName", "toName")
.exclude("fieldNamed", DefinedInClass.class)
.setFieldFilter(VisibilityFilter.ALL)
.create();
genson.serialize(myObject);
If you want to do some more complex filtering (based on annotations for example) you can implement BeanMutatorAccessorResolver or extend BaseResolver.
Same for property renaming you can implement PropertyNameResolver and have full control.
And finally if you want to filter fields, methods or constructors according to their modifiers you can define your own VisiblityFilter.
Concerning performances of filtering/renaming there should be no problem as it is done only once per class and then cached.
To start using Genson you can have a look at the Getting Started Guide.

Found solution to the problem.
Google gson has class called GsonBuilder which has methods for exclusion strategy and naming strategy.
Using these two methods implemented a custom solution, where all the mapping and exclusion rules are stored using a xml and used at the time of serialization and de-serialization.
Works perfectly, though not sure about the performance of same.

Related

Is there a way to annotate the INSTANCE field of kotlin objects?

I have a Kotlin object that has several fields exposed as static #JvmFields. The parser that I use (which I cannot edit or change) looks for public static fields and creates a configuration file based on those. Since the INSTANCE field is public too, the parser generates a new category called instance. Is there a way to add actual annotations to the INSTANCE field? I would want to add the #Ingore annotation to it so the parser does not use the INSTANCE field.
Basically, the answer is no, Kotlin does not allow annotating or altering the INSTANCE fields in any other way. If you believe this could be a useful feature, please file a feature request at kotl.in/issue.
The valid solutions to this problem are:
Make the bytecode analyzing tool Kotlin-aware, i.e. make it behave correctly with Kotlin declarations. Though this requires non-trivial job to be done and does not seem possible in your case, it could be a valuable time investment.
Create another ad-hoc tool that post-processes the classes produced by the Kotlin compiler and adds the annotations you need, then include that tool into your build.

How to implement an xml saveable/loadable class?

I would like to create a class, which would be loadable and saveable to an XML file. I want to use one class which is doing the loading task and I want to integrate it with the actual class that I want to save and load, everything seems to be doable up to the point where JAVA doesn't allow the class instance to be change from within the class, i.e. there is no:
this = JAXBLoader.load();
So currently that's the problem I'm facing.
And I want to be able to control the loading and saving it via the public methods from the class itself, so that from the outside I don't need any factories or managers to load it. Currently the only solution I've seen to this was if I extended the class that I want to save as an xml and then delegate all the methods to the intance of the actual class and then when loading a new instance from the file, the instance would get replaced. But it is a bit of overhead to have to delegate all of the methods, especially pain in the ass if you need to add new methods to the class and have multiple implementations...
So are there any good practices or patterns on achieving something similar or solving the problem I demonstrated above? Actually I'm open, if somebody can overall share what are the best ways to do class saving and loading the easiest ways I would really glad about it.
I'm not quite sure why do you want to avoid external factories and managers. For me it seems quite natural to extract serialization and not handle it in the model classes themselves. But okay.
What I understood is that your core problem is to load data into this instance. Here's a simple way to achieve this with JAXB.
I'm the author of JAXB2 Basics, a plugin package for JAXB/XJC. It contains the copyable plugin which generates a few copyTo methods in the schema-derived classes.
This will give you methods like copyTo(Object target). With this you can first unmarshal data from XML into some temporary instance and then copyTo(this). Something like:
MyType temporaryInstance = unmarshaller.unmarshal(source, MyType.class).getValue();
temporaryInstance.copyTo(this);
You can add this method to your schema-derived code via code injection or by subclassing.

jackson - ignoring case in snake case keys

I have a requirement to receive JSON with keys that contain underscore and even ignore the case in words. For e.g. Device_control_API, device_control_API, Device_Control_API, device_control_aPI etc all should map to same property.
Now I know that I can create multiple setter methods using #JsonSetter with all combinations possible, but I don't think that will be good.
I have seen other questions which suggest using mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
for ObjectMapper object to ignore case, but I can't do that because I am using spring-boot and want my REST API to get payload in the form POJO object.
Is there any annotation or some way to do so
Please help !!!
I dont think you can use the MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES as annotation. Got the following information from here
Jackson on/off features: MapperFeature
Jackson defines a set of per-mapper configuration, which can ONLY be
defined before using ObjectMapper -- meaning that these settings can
not be changed on-the-fly, on per-request basis. They configure
fundamental POJO introspection details, and resulting built objects
(serializers, deserializers, related) are heavily cached. If you need
differing settings for these, you have to use separate ObjectMapper
instances.
And the MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES is one of the configuration.
But maybe a custom deserialization class could help you. There are many tutorials and questions on Stackoverflow.
I found some:
Jackson: using #JsonSerialize (or #JsonDeserialize) annotation to register a custom serializer (or deserializer)
Right way to write JSON deserializer in Spring or extend it
There is also this property:
spring.jackson.mapper.accept_case_insensitive_properties=true

Genson Caching of Bean Accessors

I have recently upgraded to Genson 1.3 and I am not 100% sure if this issue is new or not as previously I patched the 0.98 version to make it work.
Context
We are using our own implementation of the BeanMutatorAccessorResolver. This is so that we can dynamically decide whether a property should be serialized or not. Basically we have integrated Genson into our generic jersey REST API interface. Genson does all the serializing and deserializing. When doing a GET requests it is possible for a user to pass fields in the URL in order to filter those he specifically needs (especially for large objects this is necessary where you only need 3 fields or so for displaying a table overview). For example: ?fields=field1, field2, field3. We then know in our implementation of BeanMutatorAccessorResolver exactly which fields to serialize and which ones to ignore. This is mainly intended for speeding up requests and parsing as we are then working with less data.
Problem
Unfortunately it seems that once Genson has read in all the fields via reflection or whatever, it caches that. This would be no problem if we were always requesting the same fields. Unfortunately on some occasions we need more fields then before, but because Genson does not visit our BeanMutatorAccessorResolver a second time it only returns the few fields that it has already cached.
Is there anyway around this? Perhaps there is a better solution than turning cahing off completely - because that would most probably hurt performance, right?
Update
Is seems that I have found the location where this is happening. Basically Genson returns a cached converter in Genson.provideConverter(Type forType) (line: 154).
Converter<T> converter = (Converter<T>) converterCache.get(forType);
At the top of the method I have noticed that it looks for a __GENSON$DO_NOT_CACHE_CONVERTER.
if (Boolean.TRUE.equals(ThreadLocalHolder.get("__GENSON$DO_NOT_CACHE_CONVERTER", Boolean.class))) {
Should I perhaps set this value or is there a better solution?
The problem has been solved thanks to Eugen. The solution can be found here: https://groups.google.com/forum/#!topic/genson/Z1oFHJfA-5w.
Basically you need to extend 3 classes to get this working:
GensonBundle, which you can register with the GensonBuilder.
BaseBeanDescriptorProvider, which gets created in GensonBundle.
BeanDescriptor, which gets created in BaseBeanDescriptorProvider and
which contains the serialize method to adapt to your needs.

How to extend Jackson property discovery in a "catch-all-types" way?

Following my previous question about serialization only, I'd like to go further and support JsonFormatVisitor.
I have the same requirements, that is:
I have objects of several types (interfaces).
I don't know the type of theses objets in advance.
I can't add annotations on theses types.
I can introspect all theses objets to get their state data.
Now that serialization works, I need to generate JsonSchema and hence do something like that:
SchemaFactoryWrapper visitor = WHAT?
mapper.acceptJsonFormatVisitor( mapper.constructType( Foo.class ), visitor );
JsonSchema jsonSchema = visitor.finalSchema();
String schemaString = mapper.writeValueAsString( jsonSchema );
I've implemented a SchemaFactoryWrapper that gets its expectAnyFormat called but I don't know what to do inside it. Looks like there's no schema for "any" objects.
Maybe I can hook elsewhere in jackson? Maybe it is possible to extends the whole Bean/Property introspection mechanism to support a completely different model (ie. not beans)?
I'm a bit lost, please help me find the treasure room :)
I can try to suggest some approaches that may be helpful.
First, even if you can not annotate classes directly, "mix-in annotations" can help -- this does assume static knowledge, however
Second, since schema-generation uses type detection used for serialization, you may want to register custom serializers; but this does not necessarily mean having to hand-write all. The most flexible way to register custom serializers is via Module interface (mapper.registerModule(new MyModule()); Modules can register Serializers instance which gets called when trying to locate a JsonSerializer for a type for the first time (after this, instance is cached to be re-used for other properties of same type).
This is where you could configure and return your custom JsonSerializer; but it might only need to handle schema-related callback(s) (one(s) called by schema generator).
It is also possible to extend/modify property discovery mechanism; whether this is easier depends. But the thing to look for is registering BeanSerializerModifier via Module.
It gets called during construction of BeanSerializer (general POJO serializer used unless something more specific is registered), and with it you can add/modify properties; or just replace resulting serializer altogether (and also then allows chaining of custom serializer with default one, if needed).

Categories

Resources