How to apply jackson's default behaviour in custom serializer? - jackson-databind

Since Jackson's hibernate5-module not working for me. I'm trying to implement my own lazy property filter. I implemented custom annotation introspection successfully.
But when I apply my custom serializer, #JsonIgnoreProperties is ignored.
#Entity
class Call {
#OneToMany(mappedBy = "call")
#JsonIgnoreProperties("call")
List<CallEvent> events;
}
#Entity
class CallEvent {
#ManyToOne(fetch = FetchType.LAZY)
Call call;
}
public class LazyValueIntrospector extends JacksonAnnotationIntrospector {
#Override
public Object findSerializer(Annotated a) {
var yes = a.hasAnnotation(ManyToOne.class)
|| a.hasAnnotation(Basic.class)
|| a.hasAnnotation(OneToMany.class)
|| a.hasAnnotation(OneToOne.class);
if (yes) {
return LazyValueSerializer.class;
}
return super.findSerializer(a);
}
}
public class LazyValueSerializer extends JsonSerializer<Object> {
#Override
public boolean isEmpty(SerializerProvider provider, Object value) {
return value == null || !Hibernate.isInitialized(value);
}
#Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeObject(value);
}
}
Explanation:
LazyValueIntrospector.findSerializer detects possible lazy properties.
Hibernate.isInitialized tells me the value is initialized or not.
gen.writeObject(value); writes if property is initialized.
The problem is gen.writeObject(value); method call is ignoring #JsonIgnoreProperties("call") annotation.
The question is:
How to apply #JsonIgnoreProperties("call") annotation in my custom serializer?
Ps: spring.jackson.defaultPropertyInclusion=non_empty property applied globally. Which allows isEmpty checking.

As we are ignoring only one property, try giving #JsonIgnore over the call property inside the CallEvent entity above Call.
Example:
#Entity
class CallEvent {
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
Call call;
}

Without actual example of where #JsonIgnoreProperties is ignored it is hard to say. But I think that the custom serializer would need to delegate to the original serializer and not replace it; there's quite a bit more that is needed to support various other features.
To do that you cannot register serializer the way like shown here, but instead replace it using one of methods in BeanSerializerModifier (and need to register that modifier with ObjectMapper) -- that way you get the "real" serializer to delegate to.
Usually implementations also need to implement createContextual() (from ContextSerializer) which needs to be passed to the original ("delegate") serializer.
You may want to have a look at how serializers are implemented in Hibernate module itself.

So, Since the question is how to apply jackson's own behaviors in the custom bean serializer. I found the answer for myself.
extend from BeanSerializerBase, not BeanSerializer.
override with* methods. Such as withByNameInclusion and withProperties
This way, Jackson calls appropriate methods when it's needed.
#Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer unwrapper) {
return new LazyBeanUnwrappingSerializer(this, unwrapper);
}
#Override
protected BeanSerializerBase withProperties(BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) {
return new LazyBeanSerializer(this, properties, filteredProperties);
}
#Override
protected BeanSerializerBase withByNameInclusion(Set<String> toIgnore, Set<String> toInclude) {
return new LazyBeanSerializer(this, toIgnore, toInclude);
}
#Override
public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
return new LazyBeanSerializer(this, objectIdWriter, _propertyFilterId);
}
#Override
public BeanSerializerBase withFilterId(Object filterId) {
return new LazyBeanSerializer(this, _objectIdWriter, filterId);
}
#Override
protected BeanSerializerBase asArraySerializer() {
throw new RuntimeException("Array serializer no supported");
}

Related

Grails databinding - How to exclude child property

How can I exclude the child domain property when I use the grailsWebDataBinder?
For example, I have domains:
class Car {
String carPropertyToExclude
Set<Detail> details
static hasMany = [details: Detail]
}
class Detail {
String detailPropertyToExclude
static belongsTo= [car: Car]
}
I want to exclude the detailPropertyToExclude from Detail when I call the bind method of grailsWebDataBinder and give the car instance as a parameter
Code:
List blackList = ["carPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null, blackList)
Note:
Don't suggest the bindable: false or variants when excluded from anywhere. Only need to know is there a way to do it by providing blackList as bind() method parameter.
These variants also not working:
List blackList = ["carPropertyToExclude", "details.detailPropertyToExclude"]
List blackList = ["carPropertyToExclude", [Detail.class : "detailPropertyToExclude"]]
The main question is how to prepare the blackList to exclude also child's property?
blacklist parameter supports only direct object properties
you can use DataBindingListener
import grails.databinding.events.DataBindingListenerAdapter
class BlackListener extends DataBindingListenerAdapter{
List<String> list
//returns false if you want to exclude property from binding
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
return !list.contains("${obj?.class.name}.${propertyName}".toString())
}
}
...
List blackList = ["Car.carPropertyToExclude", "Details.detailPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params),
new BlackListener(list:blackList) )
UPD:
Unfortunately the method above does not work with Collection binding.
The problem that SimpleDataBinder.setPropertyValue(...) method loses listener when processing a list.
Not sure if following workaround is good (potentially context initialization required)
but it's possible to register converter for each black list:
import grails.databinding.SimpleDataBinder
import grails.databinding.SimpleMapDataBindingSource
import grails.databinding.converters.ValueConverter
SimpleDataBinder setBlackList(SimpleDataBinder binder, Map<Class,List<String>> blackLists) {
blackLists.each { Class clazz, List<String> blackList ->
def vc = new ValueConverter(){
boolean canConvert(Object value){
return value instanceof Map
}
Object convert(Object value){
def obj = clazz.newInstance()
binder.bind( obj, new SimpleMapDataBindingSource(value), [], blackList )
return obj
}
Class<?> getTargetType(){ clazz }
}
binder.registerConverter(vc)
}
return binder
}
...
Map blackLists = [
(Car.class) : ["carPropertyToExclude"],
(Detail.class) : ["detailPropertyToExclude"]
]
setBlackList(grailsWebDataBinder,blackLists)
...
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null,
blackLists[car.getClass()] )
PS: as alternative possible to set grailsWebDataBinder.conversionService...
In a controller you can exclude props from binding by:
def someAction(){
Car car = new Car()
bindData car, params, [exclude: ['carPropertyToExclude', 'details']]
car.details = params.list('details').collect{
bindData new Detail(), [exclude: ['detailPropertyToExclude']]
}
}
You might also want to use the command-objects to represent your form-data.
I have found one solution based on daggett answer. Maybe in greater versions, the bug is fixed or will be fixed. The bug is that when we give the listener as a parameter of bind method for child domains the listener isn't triggered but when we set it as class level listener works. Grails version 3.2.11.
I have created the BlackListener like this:
public class BlackListListener extends DataBindingListenerAdapter {
private final Map<Class<?>, Collection<String>> blackList;
public BlackListListener(Map<Class<?>, Collection<String>> blackList) {
this.blackList = blackList;
}
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
Boolean result = Boolean.TRUE;
Collection<String> list = blackList.get(obj.getClass());
if (CollectionUtils.isNotEmpty(list)) {
result = !list.contains(propertyName);
}
return result;
}
}
Then I make my own grailsWebDatabinder bean as prototype:
<bean id="webDataBinder" class="grails.web.databinding.GrailsWebDataBinder" c:_0-ref="grailsApplication"
scope="prototype"/>
<bean id="carBinder" class="CarDataBinder" c:_0-ref="webDataBinder"/>
and then when I want to use data binder I inject the webDataBinder and init listener:
public CarDataBinder(GrailsWebDataBinder grailsWebDataBinder) {
this.grailsWebDataBinder = grailsWebDataBinder;
DataBindingListener blackListListener = new BlackListListener(
ImmutableMap.of(
Car.class, ImmutableSet.of("carPropertyToExclude"),
Detail.class, ImmutableSet.of("detailPropertyToExclude")
)
);
grailsWebDataBinder.setDataBindingListeners(blackListListener);
}
and then:
void bindData(Car car, Map<?, ?> params) {
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params));
}
If there are better ways you can post.

Getting an injected object using CDI Produces

I have a class (OmeletteMaker) that contains an injected field (Vegetable). I would like to write a producer that instantiates an injected object of this class. If I use 'new', the result will not use injection. If I try to use a WeldContainer, I get an exception, since OmeletteMaker is #Alternative. Is there a third way to achieve this?
Here is my code:
#Alternative
public class OmeletteMaker implements EggMaker {
#Inject
Vegetable vegetable;
#Override
public String toString() {
return "Omelette: " + vegetable;
}
}
a vegetable for injection:
public class Tomato implements Vegetable {
#Override
public String toString() {
return "Tomato";
}
}
main file
public class CafeteriaMainApp {
public static WeldContainer container = new Weld().initialize();
public static void main(String[] args) {
Restaurant restaurant = (Restaurant) container.instance().select(Restaurant.class).get();
System.out.println(restaurant);
}
#Produces
public EggMaker eggMakerGenerator() {
return new OmeletteMaker();
}
}
The result I get is "Restaurant: Omelette: null", While I'd like to get "Restaurant: Omelette: Tomato"
If you provide OmeletteMaker yourself, its fields will not be injected by the CDI container. To use #Alternative, don't forget specifying it in the beans.xml and let the container instantiate the EggMaker instance:
<alternatives>
<class>your.package.path.OmeletteMaker</class>
</alternatives>
If you only want to implement this with Producer method then my answer may be inappropriate. I don't think it is possible (with standard CDI). The docs says: Producer methods provide a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.
Thanks Kukeltje for pointing to the other CDI question in comment:
With CDI extensions like Deltaspike, it is possible to inject the fields into an object created with new, simply with BeanProvider#injectFileds. I tested this myself:
#Produces
public EggMaker eggMakerProducer() {
EggMaker eggMaker = new OmeletteMaker();
BeanProvider.injectFields(eggMaker);
return eggMaker;
}

Inherited grails domain classes missing dynamic properties

I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.

Structuremap constructor overloading

I have a command class that needs to have 2 constructors. However,
using structuremap it seems that I can only specify one constructor to
be used. I have solved the problem for now by subtyping the specific
command class, which each implementation implementing it's own
interface and constructor. Like the code below shows. The
ISelectCommand implements two separate interfaces for the
string constructor and the int constructor, just for the sake of
registering the two subtypes using structuremap.
However, I consider this a hack and I just wonder why is it not
possible for structuremap to resolve the constructor signature by the
type passed in as parameter for the constructor? Then I could register
the SelectProductCommand as an ISelectCommand and
instantiate it like:
ObjectFactury.With(10).Use>();
orObjectFactury.With("testproduct").Use>();
public class SelectProductCommand : ISelectCommand<IProduct>,
ICommand, IExecutable
{
private readonly Func<Product, Boolean> _selector;
private IEnumerable<IProduct> _resultList;
public SelectProductCommand(Func<Product, Boolean> selector)
{
_selector = selector;
}
public IEnumerable<IProduct> Result
{
get { return _resultList; }
}
public void Execute(GenFormDataContext context)
{
_resultList = GetProductRepository().Fetch(context,
_selector);
}
private Repository<IProduct, Product> GetProductRepository()
{
return ObjectFactory.GetInstance<Repository<IProduct,
Product>>();
}
}
public class SelectProductIntCommand: SelectProductCommand
{
public SelectProductIntCommand(Int32 id): base(x =>
x.ProductId == id) {}
}
public class SelectProductStringCommand: SelectProductCommand
{
public SelectProductStringCommand(String name): base(x =>
x.ProductName.Contains(name)) {}
}
P.s. I know how to tell structuremap what constructor map to use, but my again my question is if there is a way to have structuremap select the right constructor based on the parameter passed to the constructor (i.e. using regular method overloading).
The short answer is this post by the creator of Structuremap.
The long answer is regarding the structure you have in that piece of code. In my view, a command is by definition a "class" that does something to an "entity", i.e it modifies the class somehow. Think CreateNewProductCommand.
Here you are using commands for querying, if I'm not mistaken. You also have a bit of a separation of concern issue floating around here. The command posted defines what to do and how to do it, which is to much and you get that kind of Service location you're using in
private Repository<IProduct, Product> GetProductRepository()
{
return ObjectFactory.GetInstance<Repository<IProduct, Product>>();
}
The way I'd structure commands is to use CreateProductCommand as a data contract, i.e it only contains data such as product information.
Then you have a CreateProductCommandHandler which implements IHandles<CreateProductCommand> with a single method Handle or Execute. That way you get better separation of concern and testability.
As for the querying part, just use your repositores directly in your controller/presenter, alternatively use the Query Object pattern
I think I solved the problem using a small utility class. This class gets the concrete type from ObjectFactory and uses this type to construct the instance according to the parameters past into the factory method. Now on the 'client' side I use ObjectFactory to create an instance of CommandFactory. The implementation of CommandFactory is in another solution and thus the 'client solution' remains independent of the 'server' solution.
public class CommandFactory
{
public ICommand Create<T>()
{
return Create<T>(new object[] {});
}
public ICommand Create<T>(object arg1)
{
return Create<T>(new[] {arg1});
}
public ICommand Create<T>(object arg1, object arg2)
{
return Create<T>(new[] {arg1, arg2});
}
public ICommand Create<T>(object arg1, object arg2, object arg3)
{
return Create<T>(new[] {arg1, arg2, arg3});
}
public ICommand Create<T>(object[] arguments)
{
return (ICommand)Activator.CreateInstance(GetRegisteredType<T>(), arguments);
}
public static Type GetRegisteredType<T>()
{
return ObjectFactory.Model.DefaultTypeFor(typeof (T));
}
}

GSP rendering programmatically

Suppose I have a gsp snippet stored in my database. How do I programmatically merge it with a data model to produce a string.
The applicationContext of any Grails app contains a bean named
groovyPagesTemplateEngine
By default this is a instance of GroovyPagesTemplateEngine. So you might use code like this in your controller or service:
class MyService/MyController {
def groovyPagesTemplateEngine
String renderGSPToString(String uri, Map model) {
groovyPagesTemplateEngine.createTemplate(uri).make(model).toString()
}
}
NB: this snippet is not really taken from running code, it should just clarify the idea.
I found a DIRTY (but working) way of rendering complex gsps offline using groovyPageRenderer with substituted scriptsource. In that case you have access to all gsp syntax including g:if etc..
First define two dummy classes:
class StringPageLocator extends GrailsConventionGroovyPageLocator {
GroovyPageScriptSource findViewByPath(String content) {
return new StringScriptSource(content)
}
}
class StringScriptSource implements GroovyPageScriptSource{
String content
public StringScriptSource(String content) {
this.content=content
}
#Override String suggestedClassName() { "DummyName" }
#Override boolean isPublic() { true }
#Override String getScriptAsString() { return content }
#Override boolean isModified() { true }
#Override String getURI() { "DummyURI" }
}
And then you can use it as such:
def groovyPageLocator // Injected automaticaly to service/controller etc...
groovyPageRenderer.groovyPageLocator=new StringPageLocator()
String output=groovyPageRenderer.render(
view:'Hello2 ${user} <g:if test="${test}">TRUE!!!</g:if>',
model:[user:'test user2',test:true]
)
You can make a controller method that does what you want. Then you will have an HTTP api to accomplish what you want. The controller method's template will have a <g:render> tag, appropriately parameterized.

Resources