I have numerous constructors inside one of my Classes so I thought it would be a good idea to to implement Bloch's "Builder Pattern" (see http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2) for one of the Classes (Spring Project).
It's quite possible, I have missed something as I am getting an "IllegalArgumentException" when running a Test Case against the Class. Does Spring allow for this type of stuff or shall I just opt for the conventional multiple constructor approach?
java.lang.IllegalArgumentException: No serializer found for class com.AllTweets$Builder and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.AllTweets["builder"])
at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2502)
at org.codehaus.jackson.map.ObjectMapper.convertValue(ObjectMapper.java:2482)
at org.springframework.data.redis.hash.JacksonHashMapper.toHash(JacksonHashMapper.java:52)
at org.springframework.data.redis.hash.DecoratingStringHashMapper.toHash(DecoratingStringHashMapper.java:4
...
Class
public class AllTweets implements Serializable{
....
public Builder getBuilder() {
return new Builder();
}
public static class Builder {
private AllTweets build;
public Builder() {
build = new AllTweets();
}
public Builder isTweet(Tweet tweet){
build.id = tweet.getId();
return this;
}
public Builder isRetweet(Retweet retweet){
build.id = retweet.getId();
return this;
}
public AllTweets build(){
return build;
}
//Acessors
}
This exception doesn't have much to do with Spring. It only has to do with how Jackson automatically serializes your objects to JSON. You added a method getBuilder() to your class, and Jackson sees that as a rgular property of your bean that must be serialized, although it shouldn't be. So, choose another name for this method (like builder()), or annotate it with #JsonIgnore to make Jackson aware that this property must not be serialized.
That said, I really don't understand why this method is not static. You shouldn't have to create a new AllTweets object to be able to get a builder from it, to be able to build another AllTweets object. The method should thus be static, to be able to simply do
AllTweets result = AllTweets.getBuilder().isTweet(foo).isShare(bar).build();
And making it static will also solve the initial problem, because the getBuilder() method won't be considered as a bean property anymore by Jackson. See Google Guava for an example implementation of this pattern: note that the method is static, and is named builder().
Related
The following code works where my bean is using Lombok Builder annotations with a custom builder.
I am having issues with testing when I want to create this bean with values.
I can no longer use Setters (intentional) and can't just use .builder() due to the custom builder.
My question is, how do I create this bean in my tests? Note that I do want to use a builder and not looking to use Lombok's #Value annotation. The custom builder is necessary to work with Jackson.
One possibility I can think of is to add a constructor inside the custom builder which I hopefully don't need to do just for the sake of testing.
Please advice. Thanks.
Working Bean setup.
#Getter
#JsonDeserialize(builder = MyData.MyDataBuilder.class)
#Builder(builderClassName = "MyDataBuilder", toBuilder = true)
public class MyData {
#JsonProperty("some_key")
private String skey;
#JsonProperty("name")
private String name;
// needed to work with Jackson
#JsonPOJOBuilder(withPrefix = "")
static class MyDataBuilder {}
}
Test trying to create the Object. Following won't work as mentioned above.
MyData.builder()
.skey("12345")
.name("some_name")
.build();
or
MyData myData = new MyData();
myData.skey("12345");
myData.name("some_name");
You can customize the builder class to have any access level you want. Lombok makes it public by default if you don't customize it.
If the class itself and at least one constructor are public, it is possible to create instances from everywhere (not just the same package). But then there is no reason in most cases why the builder class should not be public, too: If the builder pattern is beneficial in its package, it will probably also be beneficial everywhere else.
If you don't want your class to be instantiated in other packages, then the builder should not be public (and also no constructor). However, then your test is also somehow flawed or in the wrong package.
So either make the custom builder class public, or change your test.
I am using jackson to handle JSON (de)-serialization. I have a bunch of classes which are annotated with are essentially objects holding properties and associated getters and setters.
However, I often find that at some point I want to add additional properties which I don't want to include in the (de)-serialization process. This does work using #JsonIgnore, but it strikes me as relatively ugly since I have to add the annotation everywhere and things break down as soon as I forget.
I would like to know if there is a better way to separate the ignored and serialized properties. I have the following two ideas:
Use inheritance, add the new properties to the inherited class:
// everything here should be (de)-serialized
class Base {
public int getJSONProperty() {...}
}
// nothing specific to the class Derived should be (de)-serialized
class Derived extends Base {
// *not* to be included
public SomeClass getAdditionalProperty() {...}
}
However, I don't know how to tell jackson to deserialize the Derived
objects as Bases. Is this possible (Does jackson make guarantees
regarding (non)-polymorphic serialization of classes)?
Use MixIn annotations. This would require an additional abstract
class for each existing class. Also I am not sure whether this solves
the problem. Are getters which do not appear in the MixIn base class
ignored automatically or do I need to #JsonIgnore them manually?
I've seen that you don't like the previous solution i've provided, so I'm again here to provide another way to do what you want using Gson Library. I hope to help you this time.
This is The Base Class that you want to serialize
public class Base {
public int getJSONProperty() {
return jsonProperty;
}
private int jsonProperty = 2;
}
This is The Derived Class that you don't want to serialize
public class Derived extends Base{
public String getAdditionalProperty(){
return additionalProperty;
}
private String additionalProperty = "value-not-to-serialize";
}
Using Type type = new TypeToken<Base>(){}.getType(); you can define the class to use for serialization so you get the JSON String using:
Derived derived = new Derived();
Gson gson = new Gson();
Type type = new TypeToken<Base>(){}.getType();
String jsonString = gson.toJson(derived, type);
You know Gson? it's a good library to handle JSON.
You can use transient keyword to define variable thats not must be serialized, this works with Gson (It should work well with jackson, but i'm not sure)...
class Base {
// (de)-serialized
private int jsonProperty;
// not (de)-serialized
private transient SomeClass additionalProperty;
}
I think the best approach is to add annotation or use the transient variable.
Create the inheritance only for the purpose of serialization an object complicates the application uselessly in my point of view...
I've been looking around the web for a while.
I'm trying to create an instance of a subclass dynamically, let me explain:
I have the following class:
Public abstract class Property
And a lot of sub class created from this class, for example the following two:
public class PropertyDns extends Property
Public class PropretyNetBios extends Property
I want the client to choose one of the subclass name, and then I need to create an instance of that class.
I'm going to have a lot of subclass that extends Property so switch-case statements will be exhausting:
So:
switch (user_input){
case "PropertyDns ": return new PropertyDns();
case "PropretyNetBios": return new PropretyNetBios();
.
.
.
}
will be terrible...
any ideas?
You can use one of the following two ways to achieve the result:
Create a factory method, which takes a String parameter, and based on the parameter, write up a switch to serve the required object.
The second option (and the recommended approach here) would be to dynamically create an instance from the class name using Class.newInstance
Using the second approach would make your solution easily extendable, and the addition of new classes won't need any structural changes.
A sample implementation of the second approach would be like:
public Property getProperty(String name) {
//Make sure the name contains full cannonical name of the class
return (Property) Class.forName(name).newInstance();
}
As I commented before, this problem can be solved with the Factory Pattern, if the number of classes is too large you can mix the Factory Pattern with an Annotation Processor to generate the factory. You need to create an annotation and a corresponding annotation processor.
Here is an example of the annotation you should create:
#Target(ElementType.TYPE) #Retention(RetentionPolicy.CLASS)
public #interface Property {
String name();
Class type();
}
Your property classes will look like this:
#Property(name="DnsProperty", class=Property.class)
public class DnsProperty extends Property{
...
}
You need to implement your own processor extending the class
javax.annotation.processing.AbstractProcessor
and register it at
/META-INF/services/javax.annotation.processing.Processor
The idea is to annotate each class to provide it's name, and generate the factory statements with the annotation processor, saving you from writing the exhausting switch-case statements. Explaining the annotation processors, as switch-case statements can be exhausting, so, you can read about it in the Java documentation, here is a blog post explaining annotation processors and here is the source code.
You need a Factory and a proper naming system for you classes(for example an enumeration), read about Factory pattern. This should work for you.
public class PropertyFactory {
public enum PropertyName {
DNS,
NET_BIOS
}
public Property createProperty(PropertyName name) {
switch (name) {
case DNS:
return new PropertyDns();
case NET_BIOS:
return new PropretyNetBios();
}
return null; //Or throw an exception
}
}
Currently I have a project that makes use of Spring-Hibernate and also Jackson to deal with JSON. The first time I tried to use Jackson I always got LazyInitializationException and sometimes infinite loop for multiple entities that references each other. Then I found #JsonIgnore and #JsonIdentityInfo.
Now the problem is sometimes it is needed to ignore properties but sometimes I just need those properties to be serializable. Is there a way to sometimes ignore several fields and sometimes serialize the fields at the runtime?
I found "Serialization and Deserialization with Jackson: how to programmatically ignore fields?"
But if I always have to use the mix in annotation, it would be cumbersome if an object dozens of properties to retrieve. Eg. In page1 I need propertyA, propertyB, propertyC; in page2 I need propertyA and propertyC; in page3 I only need propertyB. In those cases alone I would have to create 1 class for each page resulting in 3 classes.
So in that case is there a way to define something like:
objectA.ignoreAllExcept('propertyA');
String[] properties = {'propertyA', 'propertyC'};
objectB.ignoreAllExcept(properties); // Retrieve propertyA and propertyC
objectC.ignore(properties);
What you might be looking for is a Module. The documentation says that Modules are
Simple interface for extensions that can be registered with ObjectMappers to provide a well-defined set of extensions to default functionality.
Following is am example of how you might use them to accomplish what you want. Note, there are other ways using which this can be achieved; this is just one of them.
A simple DTO that can be used for specifying the properties to filter:
public class PropertyFilter {
public Class<?> classToFilter;
public Set<String> propertiesToIgnore = Collections.emptySet();
public PropertyFilter(Class<?> classToFilter, Set<String> propertiesToIgnore) {
this.classToFilter = classToFilter;
this.propertiesToIgnore = propertiesToIgnore;
}
}
A custom module that filters out properties based on some attribute that you store in the current request.
public class MyModule extends Module {
#Override
public String getModuleName() {
return "Test Module";
}
#Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new MySerializerModifier());
}
#Override
public Version version() {
// Modify if you need to.
return Version.unknownVersion();
}
public static class MySerializerModifier extends BeanSerializerModifier {
public BeanSerializerBuilder updateBuilder(SerializationConfig config,
BeanDescription beanDesc,
BeanSerializerBuilder builder) {
List<PropertyFilter> filters = (List<PropertyFilter>) RequestContextHolder.getRequestAttributes().getAttribute("filters", RequestAttributes.SCOPE_REQUEST);
PropertyFilter filter = getPropertyFilterForClass(filters, beanDesc.getBeanClass());
if(filter == null) {
return builder;
}
List<BeanPropertyWriter> propsToWrite = new ArrayList<BeanPropertyWriter>();
for(BeanPropertyWriter writer : builder.getProperties()) {
if(!filter.propertiesToIgnore.contains(writer.getName())) {
propsToWrite.add(writer);
}
}
builder.setProperties(propsToWrite);
return builder;
}
private PropertyFilter getPropertyFilterForClass(List<PropertyFilter> filters, Class<?> classToCheck) {
for(PropertyFilter f : filters) {
if(f.classToFilter.equals(classToCheck)) {
return f;
}
}
return null;
}
}
}
Note: There is a changeProperties method in the BeanSerializerModifier class that is more appropriate for changing the property list (according to the documentation). So you can move the code written in the updateBuilder to changeProperties method with appropriate changes.
Now, you need to register this custom module with your ObjectMapper. You can get the Jackson HTTP message converter from your application context, and get its object mapper. I am assuming you already know how to do that as you have been dealing with the lazy-initialization issue as well.
// Figure out a way to get the ObjectMapper.
MappingJackson2HttpMessageConverter converter = ... // get the jackson-mapper;
converter.getObjectMapper().registerModule(new MyModule())
And you are done. When you want to customize the serialization for a particular type of object, create a PropertyFilter for that, put it in a List and make it available as an attribute in the current request. This is just a simple example. You might need to tweak it a bit to suit your needs.
In your question, you seem to be looking for a way to specify the properties-to-filter-out on the serialized objects themselves. That, in my opinion, should be avoided as the list of properties to filter-out doesn't belong to your entities. However, if you do want to do that, create an interface that provides setters and getters for the list of properties. Suppose the name of the interface is CustomSerialized Then, you can modify the MyModule class to look for the instances of this CustomSerialized interface and filter out the properties accordingly.
Note: You might need to adjust/tweak a few things based on the versions of the libraries you are using.
I think there is a more flexible way to do it. You can configure Jackson in a such a way that it will silently ignore lazy loaded properties instead of stopping serialization process. So you can reuse the same class. Just load all necessary properties / relations and pass it to Jackson. You can try to do it by declaring your custom ObjectMapper and by turning off SerializationFeature.FAIL_ON_EMPTY_BEANS feature. Hope it helps.
You can filter out properties without modifying classes by creating a static interface for a mixin annotation. Next, annotate that interface with the #JsonFilter annotation. Create a SimpleBeanPropertyFilter and a SimpleFilterProvider. Then create an ObjectWriter with your filter provider by invoking objectMapper.writer(filterProvider)
I have the following problem:
A framework generates a class from DB table, each table column is class variable(field).
The generated class has more than 30 fields and just one constructor with no parameters.
To create an instance of that class, I have to use 30 times some setters, which is invitation for inconsistencies.
I cannot use directly constructors with parameters or Builder pattern, as I cannot edit the generated class. What's the best way to approach this - Wrapper class, thread safe methods, a classic pattern?
You can create an external Builder class, that initialises all the fields to some default value whenever you create a new Object, and then behaves like a standard Builder.
I have solved this problem for myself by making a BeanBuilder class that uses reflection on the inside. You give it your bean and then call methods like startBean, value and similar to fill your bean with data, much like building an XML tree.
If you are in love with type safety, you can make a similar class for yourself that works specifically with that bean that you have.
You can use Builder pattern. For instance, if you have Car object with field power,weight,maxSpeed,color then you can use Builder like this:
CarBuilder{
private Car car = new Car();
public CarBuilder(int power,int weight){
car.setPower(power);
car.setWeight(weight);
}
public CarBuilder setColor(String color){
car.setColor(color);
return this;
}
public CarBuilder setMaxSpeed(int maxSpeed){
car.setMaxSpeed(maxSpeed);
return this;
}
public Car build(){
return car;
}
}
You can set mandatory fields in costructor and additional fields in settters. Also you can perform some checks in build method.