I am a bit confused as to when I need to use an object mapper. I thought it should be used for mapping a result set from a DB query into objects so I created an object mapper like this:
public class PersonMapper implements ResultSetMapper<Person>
{
public Person map(int index, ResultSet resultSet, StatementContext ctx) throws SQLException
{
Person person = new Person();
person.setPersonId(resultSet.getShort("PersonId"));
person.setPersonType((PersonType) resultSet.getObject("PersonType"));
person.setPersonName(resultSet.getString("PersonName"));
person.setPersonMobile(resultSet.getString("PersonMobile"));
return person;
}
}
Then I registered it with the specific DAO like this: #RegisterMapper(PersonMapper.class)
However, it seems that everything also works without the mapper even if I make a query like this: List<Person> list = list(namedQuery("Person.findAll")); which returns a proper list.
So when exactly should I use a mapper?
if you are talking about ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper) class then we use it to save an entire POJO object as JSON string into the database, in which you need to keep the data type of the column as json while table creation...
so we use it like this
public Response saveStateRules(#Context HttpServletRequest request,StatePojo statePojo)
{
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(statePojo);
State state = new State();
state.setRulejson(json);
}
where,
public class StatePojo implements Serializable{
private Integer stateid;
private ArrayList<StateRuleCondition> fieldconditions;
private ArrayList<Integer> stateids;
private Boolean isallow;
// contains all getters and setters
}
Related
I was provided with access to a Postgres table yielding a JSONB column amongst many "standard" others and I'm creating an entity class for it without resorting to any sort of ORM frameworks such as spring-data or hibernate. To represent that column I created the necessary POJOs, e.g.
#Entity
public class MyClass {
#Id
#Column(nullable = false)
private Long id;
#Convert(converter = SomeConverter.class)
#Column(columnDefinition = "jsonb")
private Data data; //My custom POJO
///... many other fields
Then I created a simple unit test in which I use Apache DBUtils to convert the query result set to a MyClass instance:
PGSimpleDataSource ds = //...
ResultSetHandler<List<MyClass>> h = new BeanListHandler<>(
MyClass.class, new BasicRowProcessor(
new GenerousBeanProcessor()));
QueryRunner run = new QueryRunner(ds);
run.query("select * from mytable", h);
which results in the following error: Cannot set data: incompatible types, cannot convert org.postgresql.util.PGobject, suggesting that it is not able to handle the conversion between the JSONB column and MyClass. Is there any good way to tackle this problem? I was able to get around this problem in a "not so good way" by implementing my own BeanHandler:
public class MyClassHandler extends BeanHandler<MyClass> {
public AssetHandler(Class<? extends MyClass> type, RowProcessor convert) {
super(type, convert);
}
#Override
public MyClass handle(ResultSet rs) throws SQLException {
MyClass myclass = new MyClass();
myclass.setId(rs.getLong("id"));
//...
myclass.setData(new ObjectMapper().readValue(rs.getObject("data").toString());
return myclass;
}
which works but DBUtils becomes useless as I end up doing all the work myself - this wouldn't be the case if for example I could invoke super.handle(rs) and tackle only the data field as I did there, but I found no way to do it.
I want to restructure an application so that it uses REST instead of an EJB3Factory which wasn't needed in the first place and only makes trouble with the new server.
Basically I have a class Request:
public class Request {
public String name;
public String id;
public List<? extends someObject> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The Request gets created and using Gson made into a Json object:
Gson gson = new Gson();
String payload = gson.toJson(Request);
This then gets sent to the REST API on the server. There Jackson deserializes it. I do not have access to the Jackson implementation there and cannot change it to Gson.
What I am basically trying to do now is to get Jackson to use the non-default constructor to deserialize the object. I know I can annotate the non-default constructor like this:
#JsonCreator
public Request(#JsonProperty("name") String name, #JsonProperty("id")
String id, #JsonProperty("list") List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The thing is though that the field name of list is set at runtime per reflection and the Gson object that is generated might have it as scenarioName1 for one and scenarioName2 for something else.
I have looked at the different solutions provided here on Stack Overflow but none of them could provide me with a solution for my problem. This seemed most helpful but I cannot under any circumstances use a wrapper property nor can I actually map all possibilities.
Anyone got any idea?
EDIT to include examples:
Example 1:
{"name":"someName","id":"first","someScenarioName":[{...}]}
Example 2:
{"name":"someOtherName","id":"second","differentScenarioName":[{...}]}
Since I'm out of town on business that is the best I can do with right now. It's basically the last field having a different name depending on which scenario was chosen beforehand.
Maybe you can try take a look on Mapper Features. Sincerely I didn't try it yet because I'm at work and so on, but I will send now my example and maybe it can help you:
public class Request {
public String name;
public String id;
public List<? extends T> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
}
Then to deserialize the object:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.USE_ANNOTATIONS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.readValue(json, Request.class);
My try is because the deserialization by annotation is true by default, but once you don't have a "list" object most of time, it won't find the field there.
Okay, so I figured out what my problem was. There are other lists in the class and those were the trouble. After annotating each of them with #JsonProperty("theirRespectiveName") it worked like a charm... Now I have to annotate about 100 lines of code and solve some more problems.
My object graph consists of Hibernate entities. Now most of the objects don't exist in the new database. However some do. So my object is like this:
Class MyObject{
Set<B> bset;
Set<C> cset;
}
The items in bset need to be instantiated and persisted after deserialization. However, the items in cset already exist in the new database, so I don't want new instances created. What is the best way to tell Jackson I know how to find references to these? Right now I am thinking about using a custom serializer / deserializer for cset, which will serialize it by creating an object with the database ID, and then will deserialize it by pulling the appropriate objects out of the database.
This is kind of complicated and I am hoping there is a simpler solution. Any suggestions?
Figured it out. There are three things I needed:
A JsonCreator to take the entityManager, and the id to return an object
#JsonCreator
#IgnoredMethod
public static UiElement findById(#JacksonInject EntityManager entityManager, #JsonProperty("id") int id) {
return entityManager.find(UiElement.class, id);
}
A JsonValue getter to return an object with only the id
#JsonValue
#Transient
public Map<String,Integer> getJsonObject(){
HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("id", getId());
return map;
}
The entity manager needed to be injected into the ObjectMapper
//entitymanager for creating any static data based entities
InjectableValues injects = new InjectableValues.Std().addValue(EntityManager.class, entityManager);
mapper.setInjectableValues(injects);
I am using Morphia (0.99) for mapping POJO to MongoDB (2.0.6). Now I want to return a collection / list as a JSON struct from my data-store with including IDs for each object to be sent with jQuery to the browser client.
I retrieve the POJO list from the data-store and then convert each object to a DBObject MongoDB object. This works fine except that the id of the object is not included in the print-out of the object. This could be due to the the ID is inherited from the Class A. The POJO B has a lot of property members, so one idea was to make a simple proxy POJO and then Serialize.
How can I make a JSON struct from the list of DBObject(with Morphia or MongoDB driver) and how can I include the id for each element in the struct?
PS. I've bean looking at the BasicBSONList MongoDB driver object or using the GSON (Google) lib, but I am lacking the pattern for how to do this.
POJO Classes
Class A {
#Id
private ObjectId id;
}
Class B extends A {
...
#Override
public ObjectId getId() {
return super.getId();
}
#Override
public void setId(ObjectId id) {
super.setId(id);
}
...
}
Morphia /MongoDB Query
...
List<B> bList = bQry.asList();
List<DBObject> dbObjList = Util.mapObjectToStringList((List<Object>)(List<?>)bList);
...
public static List<DBObject> mapObjectToStringList(List<Object> objList) {
List<DBObject> dbObjectList = new ArrayList<DBObject>();
Mapper mapper = morph.getMapper();
for(Object obj : objList) {
DBObject dbObj = mapper.toDBObject(obj);
Util.debug("Result as DB Object: " +dbObj.toString());
dbObjectList.add(dbObj);
}
return dbObjectList;
}
Lets say that I have the following classes:
public class Person {
String name;
Set<Department> departments;
}
public class Department {
String code;
String name;
}
So I want to write a custom Department deserializer in order to annotate the deparments field in the Person class to use it. Because this custom deserializer will only be used to deserialize Department objects that are inside a Person object. The problem is that my custom Department deserializer will need to have a DepartmentRepository that must be passed in the deserializer's constructor. How can I do this? Is this possible? I don't want to register the deserializer in the object mapper because it must only be used when the deparatments field from the Person class gets deserialized.
UPDATE: What I need is, apart from annotate the departments field with JsonDeserialize annotation with the parameter contentUsing = MyCustomDepartmentDeserializer.class, is a way to tell Jackson that when it creates a MyCustomDepartmentDeserializer object, it must done it by calling a constructor that receives a DepartmentRepository. The deserializer may be something like this:
public class MyCustomDepartmentDeserializer extends JsonDeserializer<Department> {
private final DepartmentRepository departmentRepository;
public MyCustomDepartmentDeserializer(DepartmentRepository departmentRepository) {
this.departmentRepository = departmentRepository;
}
#Override
public Department deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
//IMPLEMENTATION!
}
}
First things first: to specify deserializer to use for contents of an array you can use
#JsonDeserialize(contentUsing=MyDeserializer.class)
Set<Department> departments;
to specify deserializer to use for contents of the collection in question.
As to ability to use non-default constructors, #JsonCreator allows this.
But to pass a context object, you need Jackson 1.9 may be your friend (see "Jackson 1.9 overview"), which allows "injection" of objects outside of JSON.
You can then mix and match injectable values and JSON properties, for example:
public class POJO {
#JsonCreator // can also be used for static factory methods
public POJO(#JacksonInject DepartmentRepository repo, #JsonProperty("value") int value) {
....
}
}
This might be enough to do what you are asking.
You can add a custom serializer/deserializer with a non-default constructor by registering it as a module with you ObjectMapper.
SimpleModule simpleModule = new SimpleModule();
JsonDeserializer<MyObject> customDeserializer = new CustomDeserializer("Blah");
simpleModule.addDeserializer(MyObject.class, customDeserializer);
mapper.registerModule(simpleModule);
You should also remove the annotation in the MyObject class if it's there.
Here is a deserializer I just wrote. Note the use of a non-default constructor.
public class SparseStringArrayVectorDeserializer extends JsonDeserializer<SparseStringArrayVector> {
#Override
public SparseStringArrayVector deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
/* This isn't the most efficient way to do this, since we're building a tree of nodes that we will discard.
* However, we need to change the order around, so something like this is hard to avoid.
*/
JsonNode tree = jp.readValueAsTree();
int tokenCount = tree.size();
int[] indexes = new int[tokenCount];
String[][] strings = new String[tokenCount][];
Iterator<Entry<String, JsonNode>> fieldNameIt = tree.getFields();
int slot = 0;
while (fieldNameIt.hasNext()) {
Entry<String, JsonNode> entry = fieldNameIt.next();
int index = Integer.parseInt(entry.getKey());
indexes[slot] = index;
String[] thisTokenStrings = new String[entry.getValue().size()];
for (int x = 0; x < thisTokenStrings.length; x++) {
thisTokenStrings[x] = entry.getValue().get(x).getTextValue();
}
strings[slot] = thisTokenStrings;
slot++;
}
return new SparseStringArrayVector(indexes, strings);
}
}
Used with the following. Note that you could have any constructor pattern that you like when creating the deserializer and adding it to the module.
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("ResultAccess", new Version(7, 4, 0, null));
module.addDeserializer(SparseStringArrayVector.class, new SparseStringArrayVectorDeserializer());
module.addDeserializer(AbstractResultAccess.class, new ProxyAbstractResultAccessDeserializer());
mapper.registerModule(module);
No, at the very beginning, you can go without specify a custom deserializer; Jackson can detect your nested field and map them correctly, only when all the model classes implements Serializable.
So, add implements Serializable to Department and Person, and you will see Jackson works out of the box.
Just off the top of my head, I am pretty sure you can do that using the annotations in Jackson to identify which properties you want to exposure.