I have a deserializer for a specific class which needs some ordering while reading fields.
Let's say I have two fields in my class (field1 and field2) and in order to read field2, it first needs field1.
For example for the following json data it works because when the deserializer parses field2, field1 is already set:
{"field1": 3, "field2": 4}
However if we reverse the fields:
{"field2": 4, "field1": 3}
I need to skip field2 via jp.skipChildren because field1 is not set. When field1 is parsed, Jackson should re-read and parse field2.
One option is to parse field2 instead of skipping and hold it in a variable so that when field1 is set, it can use the variable that holds data in field2. However; based on the value of field1, I may not need to parse field2 so I'm looking for a better solution since performance is critical in this part of the code.
I'm using Mapper.readValue(byte[], MyClass.class) method and it seems Jackson uses ReaderBasedJsonParser for parsing. Even though it's possible to get token position, I couldn't find a way to set token position.
Finally I found a way to do it. It's actually a workaround but it passes the tests that I wrote.
When you pass byte array to mapper.readValue it uses ReaderBasedJsonParser which iterates through the array and parse the JSON tree.
public static class SaveableReaderBasedJsonParser extends ReaderBasedJsonParser {
private int savedInputPtr = -1;
public SaveableReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st, char[] inputBuffer, int start, int end, boolean bufferRecyclable) {
super(ctxt, features, r, codec, st, inputBuffer, start, end, bufferRecyclable);
}
public void save() {
savedInputPtr = _inputPtr;
}
public boolean isSaved() {
return savedInputPtr>-1;
}
public void load() {
_currToken = JsonToken.START_OBJECT;
_inputPtr = savedInputPtr;
_parsingContext = _parsingContext.createChildObjectContext(0, 0);
}
}
When you use this JsonParser, the JsonParser instance that will be passed to your deserializer EventDeserializer.deserialize(JsonParser, DeserializationContext) will be a SaveableReaderBasedJsonParser so you can safely cast it.
When you want to save the position, call jp.save() so that when you need to go back, you can call just call jp.load().
As I said, it's actually a workaround but when you need this kind of feature and don't want to parse the tree twice for performance reasons, you may give it a try.
A custom deserializer needs uses the streaming API. There is no way to go back, reparse, etc.
Did you register the custom deserializer for the field type or for the class, that contains the fields, that need this special treatment?
If you register the deserializer for the class that contains those fields, you can just use the streaming API, read in all the fields of an instance, store them temporarily, e.g. in a HashMap, and then assign the values.
BTW: Your question smells like the XYProblem. Maybe you should post another question about the reason, you need to solve this problem here and see whether there is a better approach for it.
Related
I am having a field of type "text" in mysql and storing json data in it.
Eg:- "["android_app","iphone_app","windows_app"]";
I am interacting with mysql using hibernate and while reading this field I am deserializing it to an arraylist in Java.
My question is, is this the best and fastest way to handle such cases or there are some better ways of doing it.
If you're able to take advantage of some JPA 2.1 features, you could use anAttributeConverter to handle this for you automatically without having to deal with this in your business code.
public class YourEntity {
// other stuff
#Convert(converter = StringArrayToJsonConverter.class)
List<String> textValues;
}
Then you just define the converter as follows:
public class StringArraytoJsonConverter
implements AttributeConverter<List<String>, String> {
#Override
public string convertToDatabaseColumn(List<String> list) {
// convert the list to a json string here and return it
}
#Override
public List<String> convertToEntityAttribute(String dbValue) {
// convert the json string to the array list here and return it
}
}
The best part is this becomes a reusable component that you can simply place anywhere you need to represent a json array as a List<> in your java classes but would rather store it as JSON in the database using a single text field.
Another alternative would be to avoid storing the data as JSON but instead use a real table where that it would allow you to actually query on the JSON values. To do this, you'd rewrite that mapping using JPA's #ElementCollection
#ElementCollection
private List<String> textValues;
Internally, Hibernate creates a secondary table where it stores the text string values for the array with a reference to the owning entity's primary key, where the entity primary key and the string value are considered the PK of this secondary table.
You then either handle serializing the List<> as a JSON array in your controller/business code to avoid mixing persistence with that type of medium, particularly given that most databases have not yet introduced a real JSON data-type yet :).
i have a legacy classes with all the fields declared as strings and binded to UI fields, so that it can accept the user input. but in some cases user can enter a number values for few fields, these are also getting stored into a string.
Now i have a requirement to sort the objects based on the field. i am trying this using a reflection to make it common across the module. for the above scenario i am passing the data type name from front end and trying to cast dynamically as follows
private static Object typeCastAs(String type, Object obj){
Object result = null;
try{
Class _Class = Class.forName(type);
Object obj1 = (Object) obj;
result = _Class.cast(obj1);
}
catch(ClassNotFoundException ignore){
System.out.println("Catch exception");
}
return result;
}
But when i try to typecast it to integer it throws ClassCastException since the actual value is stored in String even though it is integer
Is there any alternate solution for this requirement
You must parse the String to an Integer using Integer.valueOf(String s).
Integer i = Integer.valueOf(string);
This can not be done by reflection, you must write logic.
I'm using the Apache Commons BeanUtils library for this. It probably does everything you need. And if you are missing something you can add your own converters.
To start, have a look at the BeanUtils class.
My java application makes use of complex object graphs that are jackson annotated and serialized to json in their entirety for client side use. Recently I had to change one of the objects in the domain model such that instead of having two children of type X it will instead contain a Set<X>. This changed object is referenced by several types of objects in the model.
The problem now is that I have a large quantity of test data in json form for running my unit tests that I need to convert to this new object model. My first thought for updating the json files was to use the old version java object model to deserialize the json data, create new objects using the new version object model, hydrate the new objects from the old objects and then finally serialize the new objects back to json. I realized though that the process of programmatically creating matching object graphs and then hydrating those object graphs could be just as tedious as fixing the json by hand since the object graphs are relatively deep and its not a simple clone.
I'm wondering how I can get around fixing these json files entirely by hand? I'm open to any suggestions even non-java based json transformation or parsing tools.
One possibility, if Objects in question are closely-enough related, structurally, is to just read using one setting of data-binding, write using another.
For example: if using Jackson, you could consider implementing custom set and get methods; so that setters could exist for child types; but getter only for Set value. Something like:
```
public class POJO {
private X a, b;
public void setA(X value) { a = value; }
public void setB(X value) { b = value; }
public X[] getValues() {
return new X[] { a, b };
}
```
would, just as an example, read structure where POJO would have two Object-valued properties, "a" and "b", but write structure that has one property "values", with JSON array of 2 Objects.
This is just an example of the basic idea that reading in (deserialization) and serialization (writing out) need not be symmetric or identical.
I have a serialized object MyObject that contains integer foo. I set a value 10 to integer foo and save the object to a file using writeObject().
I add integer bar to object MyObject. I set a value 15 to integer bar and then load the old serialized file using readObject().
The old serializable file doesn't contain integer bar so integer bar will get value 0. I want to keep the value 15 in bar if the old serializable file doesn't contain variable bar.
Should I override readObject() or how could I prevent readObject() from setting "default values" to unknown objects?
I want to do this because in the constructor I'm setting my own default values and would like to use my own default values to control versioning.
Serialization doesn't set default values it defers to Java's default value initialization scheme.
If I can sum up your question. You want serialization to merge what's in the serialized stream with the values in memory. That's not possible with Java serialization as it controls what objects to create. You can read in your serialized object, then manually write the code to merge what fields you want merged together. If your stuck on Java serialization (I'd steer clear of it if I were you), but let's say you want to continue using it.
public class MyObject {
public void merge( MyObject that ) {
// given some other instance of an object merge this with that.
// write your code here, and you can figure out the rules for which values win.
}
}
ObjectInputStream stream = new ObjectInputStream( new FileInputStream( file ) );
MyObject that = stream.readObject();
someObject.merge( that );
Viola you control which fields will be merged from that into someObject. If you want a library to do this merge for you check out http://flexjson.sourceforge.net. It uses JSON serialization, and works from Beans rather than POJO. However, there is a way to take an already populated object and overwrite values from a JSON stream. There are limitations to this. The other benefit of this is you can actually read the stream back AFTER your object structure has changed something that Java serialization can technically do, but its very very hard.
Would adding the following method to your MyObject work for you?
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
{
bar = 15; // Set a default value of 15 if it's not in the serialized output file
ois.defaultReadObject();
}
Use the keyword transient to exclude fields from serialization/deserialization.
I want to convert user input that comes as Map<String, String[]> to objects in Java. More specically I want to convert the params of a HttpServletRequest to the fields of an arbitrary domain object.
I'd like to have something like this:
Domain d = Converter.convert(params, new Domain());
If there is more than one element in the string array, which is the value of a map entry, it should be converted to a list or array. Maybe the locale should be considered for date and currency conversion. And a list of conversion errors would be nice.
Is there a library with such a converter?
Would you call it "converter"? I think it is often called "data binding", but that is the wrong term in my opionion, since it is related to binding model values to GUI elements, what is a slightly different thing - isn't it?
If your web framework does not support this functionality have a look at
http://commons.apache.org/beanutils/ ,espeically the beanutils package which has classes with similar purposes (maybe exactly the same) that you want.
You may also consider switching to a more mature framework ;-)
Don't use this plain code as it is only an example. You should add some pretty exception handling and a loop through a map. But generally the idea is like this:
void putValue(String name, String value, Object object) throws Exception {
String setterName = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
Method m = object.getClass().getMethod(setterName, String.class);
if (m!=null) {
m.invoke(object, value);
}
}
This code, given a parameter name 'name' will try to find a method setName(String name) and call it with the given value.