Jackson Change JsonIgnore Dynamically - java

I have a class and there are variables inside it as well. Sometimes I want to ignore some fields and sometimes not when deserializing (maybe at serializing too). How can I do it at Jackson?

For serialization, "filtering properties" blog entry should help. Deserialization side has less support, since it is more common to want to filter out stuff that is written.
One possible approach is to sub-class JacksonAnnotationIntrospector, override method(s) that introspect ignorability of methods (and/or fields) to use whatever logic you want.
It might also help if you gave an example of practical application, i.e what and why you are trying to prevent from being deserialized.

You might want to use JsonViews ( took it originally from http://wiki.fasterxml.com/JacksonJsonViews - broken now - web archive link: https://web.archive.org/web/20170831135842/http://wiki.fasterxml.com/JacksonJsonViews )
Quoting it:
First, defining views means declaring classes; you can reuse existing ones, or just create bogus classes -- they are just view identifiers with relationship information (child inherits view membership from parents):
// View definitions:
class Views {
static class Public { }
static class ExtendedPublic extends PublicView { }
static class Internal extends ExtendedPublicView { }
}
public class Bean {
// Name is public
#JsonView(Views.Public.class) String name;
// Address semi-public
#JsonView(Views.ExtendPublic.class) Address address;
// SSN only for internal usage
#JsonView(Views.Internal.class) SocialSecNumber ssn;
}
With such view definitions, serialization would be done like so:
// short-cut:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
// or fully exploded:
objectMapper.getSerializationConfig().setSerializationView(Views.Public.class);
// (note: can also pre-construct config object with 'mapper.copySerializationConfig'; reuse)
objectMapper.writeValue(out, beanInstance); // will use active view set via Config
// or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
and result would only contain 'name', not 'address' or 'ssn'.

You should probably look at the modules feature of recent Jackson versions.
One possible mechanism would be to use a BeanDeserializerModifier.
I've been looking for a useful online tutorial or example, but nothing immediately appears. It might be possible to work something up if more is known of your context. Are you managing your ObjectMappers manually, or using them in a JAX-RS setting, injected in Spring, or what?

I searched the entire web (yes I did) to find the answer. then I wrote something on my own.
I'm working with Jackson ion deserialisation. I wrote a custom reader that ignores the fields dynamically.
You can do the same thing for json deserialisation.
Lets assume an entity like this.
User {
id
name
address {
city
}
}
Create a tree structure to represent field selection.
public class IonField {
private final String name;
private final IonField parent;
private final Set<IonField> fields = new HashSet<>();
// add constructs and stuff
}
Custom Ion Reader extending from amazon ion-java https://github.com/amzn/ion-java
public class IonReaderBinaryUserXSelective extends IonReaderBinaryUserX {
private IonField _current;
private int hierarchy = 0;
public IonReaderBinaryUserXSelective(byte[] data, int offset, int length,
IonSystem system, IonField _current) {
super(system, system.getCatalog(), UnifiedInputStreamX.makeStream(data, offset, length));
this._current = _current;
}
#Override
public IonType next() {
IonType type = super.next();
if (type == null) {
return null;
}
String file_name = getFieldName();
if (file_name == null || SystemSymbols.SYMBOLS.equals(file_name)) {
return type;
}
if (type == IonType.STRUCT || type == IonType.LIST) {
IonField field = _current.getField(getFieldName());
if (field != null) {
this._current = field;
return type;
} else {
super.stepIn();
super.stepOut();
}
return next();
} else {
if (this._current.contains(file_name)) {
return type;
} else {
return next();
}
}
}
#Override
public void stepIn() {
hierarchy = (hierarchy << 1);
if (getFieldName() != null && !SystemSymbols.SYMBOLS.equals(getFieldName())) {
hierarchy = hierarchy + 1;
}
super.stepIn();
}
#Override
public void stepOut() {
if ((hierarchy & 1) == 1) {
this._current = this._current.getParent();
}
hierarchy = hierarchy >> 1;
super.stepOut();
}
Construct dynamic view. This Tree dynamically created and passed to the reader to deserialise.
Let's say we only need city inside the address.
IonField root = new IonField("user", null);
IonField address = new IonField("address", root);
IonField city = new IonField("city", address);
address.addChild(city);
root.addChild(id);
//now usual stuff.
IonFactory ionFactory = new IonFactory();
IonObjectMapper mapper = new IonObjectMapper(ionFactory);
File file = new File("file.bin"); // ion bytes
byte[] ionData = Files.readAllBytes(file.toPath());
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonReader ionReader = new IonReaderBinaryUserXSelective(ionData, 0, ionData.length, ionSystem, root);
User user = mapper.readValue(ionReader, User.class);

Related

Order YAML file entries according to its java equivalent class (SnakeYaml)

I am using SnakeYaml to both load/dump data in Java. For this I have created a custom class with fields, say that the class looks something like this:
public class Person {
private String name;
private String lastName;
private String address;
public Person() {
// Do nothing
}
// Getters and setters initialized for all the fields
}
Now, what I would like is that when I write a Person object to a file with SnakeYaml I would want to have the fields in the order they are defined in the class.
e.g.
name: Patrick
lastName: Star
Age : 42
The problem is that for more advanced examples, this ordering is not achieved. Currently I am writing/dumping to a yaml file like the following:
Constructor struct = new Constructor(YamlIteratorModel.class);
Yaml yaml = new Yaml(struct);
try {
String path = "Some/File/Path/yamlfile.yaml";
FileWriter writer = new FileWriter(path);
yaml.dump(iteratorModel, writer);
} catch (IOExcepton e) {
// Do something
}
What I have also tried is creating a Representer class which extends Representer and calls the Yaml constructor in a similar manner. This one is taken from another post, and it doesn't do the job for me as it only sorts the Properties in an order I am not entirely sure of (can't find the link right now but will update if I find it again)..
public class ConfigurationModelRepresenter extends Representer {
/**
* Create object without specified dumper object
*/
public ConfigurationModelRepresenter() {
super();
}
/**
* Create object with dumper options
*
* #param options
*/
public ConfigurationModelRepresenter(DumperOptions options) {
super(options);
}
/** {#inheritDoc} */
#Override
protected Set<Property> getProperties(Class< ? extends Object> type) {
Set<Property> propertySet;
if (typeDefinitions.containsKey(type)) {
propertySet = typeDefinitions.get(type).getProperties();
} else {
propertySet = getPropertyUtils().getProperties(type);
}
List<Property> propsList = new ArrayList<>(propertySet);
Collections.sort(propsList, new BeanPropertyComparator());
return new LinkedHashSet<>(propsList);
}
class BeanPropertyComparator implements Comparator<Property> {
#Override
public int compare(Property p1, Property p2) {
// p1.getType().get
if (p1.getType().getCanonicalName().contains("util") && !p2.getType().getCanonicalName().contains("util")) {
return 1;
} else if (p2.getName().endsWith("Name") || p2.getName().equalsIgnoreCase("name")) {
return 1;
} else {
return -1;
}
}
}
}
SUMMARY: How do I maintain the ordering when dumping an object to a YAML file (using SnakeYaml) e.g. the order the fields appear defined in the custom class?
See this question, which discusses that you cannot get the line number of a declared field via reflection.
Together with the fact that reflection gives you a classes' fields in no particular order, it is obvious that it is not possible to observe the order of declared fields in a class at runtime, and it follows that you cannot order the keys in your YAML output according to their position/order in the source, because you cannot know that order.
The remedy is to transport the knowledge of the order to the runtime. Some possible ways to do this might be:
Annotate each field with a weight that defines the position of the resulting YAML key (ugly because you need annotations on the fields).
Autogenerate code by parsing the class' definition discovering the order from there, and write it to some autogenerated source file whose code is then used to order the properties in your Representer (this solution, while avoiding bloat in the original class, is very complex and elaborate).
Hard-code the field order in the Representer. That's basically the previous solution but without autogenerating. Error-prone because the Representer must be adjusted each time the class is changed.
I recommend against using any of those solutions. The YAML spec specifically says that key order must not convey content information, and if the order is important to you, you are already violating the YAML spec and should switch to a format that better serves your needs.

Spring Integration Message History

I have setup #EnableMessageHistory
I have created custom transformers like this
public class FileMoveTransformer implements GenericTransformer<CustomerPojo, CustomerPojo> {
private boolean renameFile;
private String toLocation;
private static final Logger LOGGER = LoggerFactory.getLogger(FileMoveTransformer.class);
public FileMoveTransformer(String toLocation, final boolean renameFile) {
this.toLocation = toLocation;
this.renameFile = renameFile;
}
#Override
public CustomerPojo transform(CustomerPojo input) {
return input;
}
}
When I look at the Message history its showing like this
How do I change the "name" attribute to my own transformer as above will make not sense to print.
The MessageHistory makes it based on the bean name:
private static Properties extractMetadata(NamedComponent component) {
Entry entry = new Entry();
String name = component.getComponentName();
String type = component.getComponentType();
if (name != null && !name.startsWith("org.springframework.integration")) {
entry.setName(name);
if (type != null) {
entry.setType(type);
}
}
if (!entry.isEmpty()) {
entry.setTimestamp(Long.toString(System.currentTimeMillis()));
}
return entry;
}
Since you don't provide an explicit id for the endpoint which uses your FileMoveTransformer, you get that generated bean name based on the endpoint ConsumerEndpointFactoryBean class.
Since you don't show how you use this FileMoveTransformer, I only can abuse guessing that it is about an IntegrationFlow and you have something like this:
.transform(new FileMoveTransformer())
So, consider to add an id there like:
.transform(new FileMoveTransformer(), e -> e.id("fileMoveTransformer"))
https://docs.spring.io/spring-integration/reference/html/java-dsl.html#java-dsl-endpoints
Otherwise, please, share how you use it and we will let you know what need to be changed to bring your own custom id for the component and make your message history much readable.

Spring-Boot: a #Component class to hold a List of Database table objects

I need to find a proper solution to have a Spring-Boot #Component (singleton) class hold a List of database table objects, which could be accessed throughout the life of an application. I need to get a value of a certain language column value (there could be many language columns) depending on the parameters.
My idea was to do it like this:
#Component
public class CardTypeValueComponent {
private List<CardTypesTabModel> listOfCardTypes;
private CardTypesModelRepository cardTypesModelRepository;
private static final String UNKNOWN = "-";
#Autowired
public CardTypeValueComponent(CardTypesModelRepository cardTypesModelRepository) {
Assert.notNull(cardTypesModelRepository, "CardTypesModelRepository cannot be null");
this.cardTypesModelRepository = cardTypesModelRepository;
}
#PostConstruct
private void getAllCardTypesFromDb() {
this.listOfCardTypes = cardTypesModelRepository.findAll();
}
public String getCardTypeLanguageValue(int cardType, String language) {
String cardTypeLangValue = UNKNOWN;
for (CardTypesTabModel cardTypesTabModel : listOfCardTypes) {
if (cardTypesTabModel.getTypeId() == cardType && "spanish".equals(language)) {
cardTypeLangValue = cardTypesTabModel.getSpanishValue();
} else {
cardTypeLangValue = cardTypesTabModel.getEnglishValue();
}
}
return cardTypeLangValue;
}
}
Is it a proper way of completing such a task whilst keeping in mind that the table object column count could increase in the future?
Excuse me for the pseudo code. Thanks.
Added more details:
CardTypesTabModel Entity class:
#Entity
public class CardTypesTabModel {
private int type;
private String englishValue;
private String spanishValue;
// other values, getters & setters
}
What you're trying to do is re-inventing the caching mechanisme.
You may consider to relay on the Spring Cache Abstraction http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html then choose JCache (JSR-107) as implementation.

Java converting from Object to Subclass

Here is my code for Scene.java. It has different types of objects, all of which are included in one common ArrayList called targets. All of them share a toString() method that returns their identifier. I want to use the targets list to determine if there is any object in the scene that matches a given identifier, regardless of its type:
ArrayList<NPC> npcs = new ArrayList<NPC>();
ArrayList<Item> items = new ArrayList<Item>();
ArrayList<EnviromentalObject> enviromental_objects = new ArrayList<EnviromentalObject>();
ArrayList<Object> targets;
public Object check_for_target(String target_name){
targets.addAll(npcs);
targets.addAll(items);
targets.addAll(enviromental_objects);
for (Object target : targets){
if (target.toString() == target_name){
return target;
}
}
return null;
Here is the code in Game.java, which checks for a given identifier. If there is a match ion the current scene, I want to know the object's type and treat it as its true type. Right now, I have the following code, and I knew it wouldn't work, but maybe it'll help get my idea across.
Object target = current_scene.check_for_target(target_name);
if (target == null){
System.out.println(UNRECOGNIZED_TARGET_MESSAGE);
} else {
String target_type = target.getClass().getName();
target = (target_type) target;
}
What would be the correct way of getting the object's type and then being able to use that object's methods? Right now, I'm only given Object's methods. Do I create a superclass for NPC, Item, and EnviromentalObject?
Basically, you can check if an object is an instance of a specific class.
it could be something like this :
if( target instanceof NPC) {
System.out.println("target is a NPC");
}
else if( Target instanceof Item) {
System.out.println("target is an Item");
}
if( target instanceof EnviromentalObject) {
System.out.println("target is EnviromentalObject");
}
Edit: as we talked in the comments I think you can change your code to reach a better solution. The above code is still works but it can be a very good practice to using Design Patterns that are known as best practices in programming. For this situation think about using java interface and define share methods that each object could implements them by its need. In the simplest way they print their identifier. Let's use an example :
public interface SceneThings() {
public void printIdentifire();
public String doSomeOtherThings();
}
Each object can implements the above interface by it needs like :
public class Item implements SceneThing {
...
public void printIdentifire(){
//print its identifier here.
System.out.print("ID:ITEM###");
}
public String doSomeOtherThings(){
//do some other works !!!
}
...
}
for other items same as above. And then you can use a single array to keep them without worry about their origin class like this:
ArrayList<SceneThings> targets = new ...
SceneThing obj = new Item();
targets.add(obj);
I hope this can help you to define a better solution in your case.
One of the ways how it could be done it to declare a superclass or interface Target and use it to keep targets array, the full code sample with abstract class:
ArrayList<NPC> npcs = new ArrayList<NPC>();
ArrayList<Item> items = new ArrayList<Item>();
ArrayList<EnviromentalObject> enviromental_objects = new ArrayList<EnviromentalObject>();
ArrayList<Target> targets;
public Target check_for_target(String target_name) {
targets.addAll(npcs);
targets.addAll(items);
targets.addAll(enviromental_objects);
for (Target target : targets) {
if (target.toString().equals(target_name)) {
return target;
}
}
return null;
}
private abstract class Target {}
private class NPC extends Target {}
private class Item extends Target {}
private class EnviromentalObject extends Target {}

ORM supporting immutable classes

Which ORM supports a domain model of immutable types?
I would like to write classes like the following (or the Scala equivalent):
class A {
private final C c; //not mutable
A(B b) {
//init c
}
A doSomething(B b) {
// build a new A
}
}
The ORM has to initialized the object with the constructor. So it is possible to check invariants in the constructor. Default constructor and field/setter access to intialize is not sufficient and complicates the class' implementation.
Working with collections should be supported. If a collection is changed it should create a copy from the user perspective. (Rendering the old collection state stale. But user code can still work on (or at least read) it.) Much like the persistent data structures work.
Some words about the motivation. Suppose you have a FP-style domain object model. Now you want to persist this to a database. Who do you do that? You want to do as much as you can in a pure functional style until the evil sides effect come in. If your domain object model is not immutable you can for example not share the objects between threads. You have to copy, cache or use locks. So unless your ORM supports immutable types your constrainted in your choice of solution.
UPDATE: I created a project focused on solving this problem called JIRM:
https://github.com/agentgt/jirm
I just found this question after implementing my own using Spring JDBC and Jackson Object Mapper. Basically I just needed some bare minimum SQL <-> immutable object mapping.
In short I just use Springs RowMapper and Jackson's ObjectMapper to map Objects back and forth from the database. I use JPA annotations just for metadata (like column name etc...). If people are interested I will clean it up and put it on github (right now its only in my startup's private repo).
Here is a rough idea how it works here is an example bean (notice how all the fields are final):
//skip imports for brevity
public class TestBean {
#Id
private final String stringProp;
private final long longProp;
#Column(name="timets")
private final Calendar timeTS;
#JsonCreator
public TestBean(
#JsonProperty("stringProp") String stringProp,
#JsonProperty("longProp") long longProp,
#JsonProperty("timeTS") Calendar timeTS ) {
super();
this.stringProp = stringProp;
this.longProp = longProp;
this.timeTS = timeTS;
}
public String getStringProp() {
return stringProp;
}
public long getLongProp() {
return longProp;
}
public Calendar getTimeTS() {
return timeTS;
}
}
Here what the RowMapper looks like (notice it mainly delegats to Springs ColumnMapRowMapper and then uses Jackson's objectmapper):
public class SqlObjectRowMapper<T> implements RowMapper<T> {
private final SqlObjectDefinition<T> definition;
private final ColumnMapRowMapper mapRowMapper;
private final ObjectMapper objectMapper;
public SqlObjectRowMapper(SqlObjectDefinition<T> definition, ObjectMapper objectMapper) {
super();
this.definition = definition;
this.mapRowMapper = new SqlObjectMapRowMapper(definition);
this.objectMapper = objectMapper;
}
public SqlObjectRowMapper(Class<T> k) {
this(SqlObjectDefinition.fromClass(k), new ObjectMapper());
}
#Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> m = mapRowMapper.mapRow(rs, rowNum);
return objectMapper.convertValue(m, definition.getObjectType());
}
}
Now I just took Spring JDBCTemplate and gave it a fluent wrapper. Here are some examples:
#Before
public void setUp() throws Exception {
dao = new SqlObjectDao<TestBean>(new JdbcTemplate(ds), TestBean.class);
}
#Test
public void testAll() throws Exception {
TestBean t = new TestBean(IdUtils.generateRandomUUIDString(), 2L, Calendar.getInstance());
dao.insert(t);
List<TestBean> list = dao.queryForListByFilter("stringProp", "hello");
List<TestBean> otherList = dao.select().where("stringProp", "hello").forList();
assertEquals(list, otherList);
long count = dao.select().forCount();
assertTrue(count > 0);
TestBean newT = new TestBean(t.getStringProp(), 50, Calendar.getInstance());
dao.update(newT);
TestBean reloaded = dao.reload(newT);
assertTrue(reloaded != newT);
assertTrue(reloaded.getStringProp().equals(newT.getStringProp()));
assertNotNull(list);
}
#Test
public void testAdding() throws Exception {
//This will do a UPDATE test_bean SET longProp = longProp + 100
int i = dao.update().add("longProp", 100).update();
assertTrue(i > 0);
}
#Test
public void testRowMapper() throws Exception {
List<Crap> craps = dao.query("select string_prop as name from test_bean limit ?", Crap.class, 2);
System.out.println(craps.get(0).getName());
craps = dao.query("select string_prop as name from test_bean limit ?")
.with(2)
.forList(Crap.class);
Crap c = dao.query("select string_prop as name from test_bean limit ?")
.with(1)
.forObject(Crap.class);
Optional<Crap> absent
= dao.query("select string_prop as name from test_bean where string_prop = ? limit ?")
.with("never")
.with(1)
.forOptional(Crap.class);
assertTrue(! absent.isPresent());
}
public static class Crap {
private final String name;
#JsonCreator
public Crap(#JsonProperty ("name") String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
}
Notice in the above how easy it is to map any query into immutable POJO's. That is you don't need it 1-to-1 of entity to table. Also notice the use of Guava's optionals (last query.. scroll down). I really hate how ORM's either throw exceptions or return null.
Let me know if you like it and I'll spend the time putting it on github (only teste with postgresql). Otherwise with the info above you can easily implement your own using Spring JDBC. I'm starting to really dig it because immutable objects are easier to understand and think about.
Hibernate has the #Immutable annotation.
And here is a guide.
Though not a real ORM, MyBatis may able to do this. I didn't try it though.
http://mybatis.org/java.html
AFAIK, there are no ORMs for .NET supporting this feature exactly as you wish. But you can take a look at BLTookit and LINQ to SQL - both provide update-by-comparison semantics and always return new objects on materialization. That's nearly what you need, but I'm not sure about collections there.
Btw, why you need this feature? I'm aware about pure functional languages & benefits of purely imutable objects (e.g. complete thread safety). But in case with ORM all the things you do with such objects are finally transformed to a sequence of SQL commands anyway. So I admit the benefits of using such objects are vaporous here.
You can do this with Ebean and OpenJPA (and I think you can do this with Hibernate but not sure). The ORM (Ebean/OpenJPA) will generate a default constructor (assuming the bean doesn't have one) and actually set the values of the 'final' fields. This sounds a bit odd but final fields are not always strictly final per say.
SORM is a new Scala ORM which does exactly what you want. The code below will explain it better than any words:
// Declare a model:
case class Artist ( name : String, genres : Set[Genre] )
case class Genre ( name : String )
// Initialize SORM, automatically generating schema:
import sorm._
object Db extends Instance (
entities = Set() + Entity[Artist]() + Entity[Genre](),
url = "jdbc:h2:mem:test"
)
// Store values in the db:
val metal = Db.save( Genre("Metal") )
val rock = Db.save( Genre("Rock") )
Db.save( Artist("Metallica", Set() + metal + rock) )
Db.save( Artist("Dire Straits", Set() + rock) )
// Retrieve values from the db:
val metallica = Db.query[Artist].whereEqual("name", "Metallica").fetchOne() // Option[Artist]
val rockArtists = Db.query[Artist].whereEqual("genres.name", "Rock").fetch() // Stream[Artist]

Categories