DSL-Json in Java project is too slow - java

When implementing dsljson in Java project, I see this is not correct. It is quite slow and hard to implement.
I create new object which implements from JsonObject
public static class abc implements JsonObject {
public final int x;
public final String s;
public abc(int x, String s) {
this.x = x;
this.s = s;
}
public void serialize(JsonWriter writer, boolean minimal) {
//parse the instance of object (abc) to json-string
}
public static final JsonReader.ReadJsonObject<abc> JSON_READER =
new JsonReader.ReadJsonObject<abc>() {
public abc deserialize(JsonReader reader) throws IOException {
// Use jsonreader and common json converter (numberconverter,
// stringconverter) to parse json-string to an
// instance of object (abc)
}
};
}
I create new : DslJson<Object> dslJson = new DslJson<Object>(); to call "deserialize"/"serialize" when use it.
I think, my implementation is not correct hence it is too slow.
So if you have any experiences or example on for this lib then can you help me provide your ideas about that?
Do we have another way to use dsljson?
DslJson cannot use like JackSon?
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{\"age\":33,\"messages\":[\"msg 1\",\"msg 2\"],
\"name\":\"mkyong\"}";
User user1 = mapper.readValue(jsonInString, User.class);

There are few examples in the library repository, eg: https://github.com/ngs-doo/dsl-json/blob/master/examples/Maven/src/main/java/com/dslplatform/maven/Example.java#L82
Some things to try:
reuse dslJson instance - it's costly to recreate it multiple times during testing
avoid using Strings - byte[] or Streams are much more GC friendly data types
If DslJson is not the fastest, most likely there is something wrong with your setup :) (which means you should show more code - how exactly are you testing/benching library)

Related

Create Generic class/method to map one object to another

Since I'm a newbie, I would like to know if there is a better way to code this.
Let say we have batch (spring) where we have downloader/processor/mapper/writer for every type of file we receive since we have customized logic for each file type. X number of Mapper , X number of processor for X number of file types.
Currently looking into templatize the code so not much changes may be required when new type is introduced. Below is my idea. so let say mapper, we have different objects for different file types and all of them will be converted to object of Class CustomObject as below. mapper bean in sample spring context
bean id = "file1Mapper" class = "com.filemapper.file1Mapper"
and it invokes file1Mapper class which has mapping logic. Same for other files.
This is what I'm coming up with to avoid all those file1mapper, file2mapper...... instead one generic mapper which does all together, but looking for better solutions,
public class GMapper{
public <T> CustomObject map(T item){
CustomObject customObject = new CustomObject()
.WithABCDetails(getABCDetails(item));
}
private <T> XYZDetails getABCDetails(T item) {
ABCDetails details = new ABCDetails();
if( item instanceof A){
A a = (A)item;
// read a and map it to ABCDetails object
}
if( item instanceof B){
B b = (B)item;
// read b and map it to ABCDetails object
}
...
...
// repeat this if loop for mapping all file types.
return details;
}
}
Sample jsons
class ABCDetails{
// JsonProperty
Object1 ob1;
Object2 ob2;
Integer d;
}
class Object1{
// JsonProperty
Object3 ob3;
String abc;
String def;
}
class Object2{
// JsonProperty
String ab;
Integer e;
}
class A{
// JsonProperty
String e;
String d; // ex, this is mapped to Object 2 String "ab"
}
This does't look so professional and I believe there might be better ways to do it. Can someone please share an example or explanation on how can this code be made better. I also reading Functional interface to see if that could help.
Thanks in advance.
It is impossible to understand what you need. So I will give some common advice.
Format your code - use tabs/spaces to indent.
Do not put capital letters together - replace ABCDetails with AbcDetails. No one cares how real world name looks like.
Do not write meaningless comments - say no to // JsonProperty
Name variables so that someone can understand what they are supposed to store - avoid {Object1 ob1; Object2 ob2; Integer d;}
Do not write if ... else if ... else if ... or case when ... since this scales badly. Use Map. Examples below.
And a general solution to your problem: use plugin architecture - the best thing (and maybe the only thing) that OOP can offer. Just make all your processors implement common interface. And to work with plugins use dispatcher pattern.
First create all processors.
public interface FileProcessor {
String extension();
void process(String filename);
}
#Component
public final class CsvFileProcessor implements FileProcessor {
public String extension() {
return "csv";
}
public void process(String filename) {
/* do what you need with csv */
}
}
#Component
public final class JsonFileProcessor implements FileProcessor {
public String extension() {
return "json";
}
public void process(String filename) {
/* do what you need with json */
}
}
Then inject them into your dispatcher. Do not forget to process errors, for example, some files may not have suffix, for some files you will not have processor, etc.
#Component
public final class FileDispatcher {
private final Map<String, FileProcessor> processorByExtension;
#Autowired
public FileDispatcher(List<FileProcessor> processors) {
processorByExtension = processors.stream().collect(Collectors.toMap(p -> p.extension(), p -> p));
}
public void dispatch(String filename) {
String extension = filename.split("//.")[1];
processorByExtension.get(extension).process(filename);
}
}
Now if you need to support new file format you have to add only one class - implementation of FileProcessor. You do not have to change any of already created classes.

Specifying a class that uses T

I'm using a method that takes a Class<T> as a parameter.
The class I want to pass as a parameter also uses T. It is declared as public class MyObject<T> and has a member declared as public T mMyVar; I then have 2 classes I sometimes use for mMyVar called MyVarObject1 and MyVarObject2.
Example:
private class MyObject<T> {
public T mMyVar;
}
private class MyVarObject1 {
// some variables
}
private class MyVarObject2 {
// some variables
}
Specifically, the method I'm invoking is the JacksonUtil method fromJsonArray.
I'm not sure of the proper syntax here. JacksonUtil needs to know the exact model structure so it can parse the json, but I'm having trouble figuring out the proper syntax for this line:
MyObject<MyVarObject1> result = JacksonUtil.fromJsonArray(jsonStr, MyObject<MyVarObject1>.class);
What I have there doesn't work. My IDE selects the second parameter and says, "Cannot select from parameterized type."
I had a same problem while using with retrofit, This is my solution -
public class ResponseDS<T> {
public int s;
public String e;
public T d;
}
And if you need array of object then,
public class ResponseDSs<T> {
public int s;
public String e;
public T[] d;
}
And below is how I am using it for Retrofit -
Call<ResponseDS<UserDS>> userModelCall = ZivaUtils.getRetrofit().getUser();
I think you have the same problem, hope my solution will help you :)
I do TypedToken from Gson to parse custom objects, I think you can find something similar to use with Jackson, i will edit my answer if i find something later.
You may use TypeToken to load the json string into a custom object.
Gson gson = new Gson();
//This is an example, you probably get this from your server as Json String
MyObject<MyObject1> user = new MyObject<MyObject1>();
String myObjectAsString = gson.toJson(user);
//then parse into your custom object
MyObject other = gson.fromJson(myObjectAsString, new TypeToken<MyObject<MyObject1>>(){}.getType());

How can I pull values from a deserialized feed when its class isn't known at runtime?

I'm using Jackson to deserialize feeds and pull specific values from it. I'm trying to do this without know what feeds will be selected at runtime. I attempted to make a generic class that contains the method for deserializing the feed, and it works oddly enough. The problem is I can't pull any information from it without explicitly stating which class I am pulling from.
public class Motor<T> {
final ObjectMapper jsonMapper = new ObjectMapper();
public Object pullJson( String url, Object blah ) throws IOException{
#SuppressWarnings("unchecked")
T mapped = jsonMapper.readValue(new URL(url), (Class<T>) blah);
return mapped;
}
//Some other code
}
public class Root{
public static String value;
public String getValue(){
return value;
}
}
public class Propeller {
public static void main(String[] args) throws IOException{
Motor<?> motor = new Motor();
List<Class<?>> classes = new ArrayList<Class<?>>();
classes.add(Root.class)
String url = "blah.com/blah.json"
Object blah = classes.get(0);
blah = motor.pullJson(url, blah);
System.out.println(blah.value);
}
}
The only way I could think to pass a class into the method without know what the class will be was to set the class parameter as an Object and then pass a generic class onto it.
If I we're to ask for Root.value, it'll give me value's value, but I won't know what class the method will act on at runtime. And trying to act on the class declared as blah will tell me value cannot be resolved. I have a feeling my thought process here is extremely flawed in trying to pass a class as an object and reading the values that way.

How to serialize object to JSON string using classes from "org.json" package?

I've tried to find any useful example to solve this easy task, however, nothing really helpful was found. I'm at the beginner level in Java and I definitely need some help.
I have two simple Java classes:
private class FMList {
public List<FMItem> items = new ArrayList();
public long size = 0;
public int dirs = 0;
public int files = 0;
public String path = "";
}
private class FMItem {
public boolean is_dir = false;
public String[] ascii = {};
public String name = "";
public String mode = "";
public long size = 0;
public long mtime = 0;
public boolean ext = false;
public String cache = "";
}
My application (applet) creates an instance of FMList, sets data to object properties, and finally serializes the object to JSON string.
First, I tried Google Gson library. Simple new Gson().toJson(fmList) did the job perfectly, however my applet failed to start giving exceptions that Gson library has somewhat like security problems. Meanwhile, the applet was signed and all AccessController's were set.
Then, I tried to use classes from JSON official website. There are no exceptions anymore but that simple new JSONObject(fmList).toString() gives {} only. I understand that it should be used somehow in a different way.
What is the right way to serialize this simple object to JSON string using classes from "org.json" package?
Thank you for help!
For field access like you are using (not get/set methods) you have to specify them:
http://www.json.org/javadoc/org/json/JSONObject.html#JSONObject(java.lang.Object, java.lang.String[])
You either use the JSON Writer or "Stringer" (write to stream or create String) to not get a pretty printed json.
http://www.json.org/javadoc/org/json/JSONWriter.html
http://www.json.org/javadoc/org/json/JSONStringer.html
Update: Also the class can't be private with the simple org.json lib.
This works fine:
public class Main {
public static class FMList {
public long size = 0;
public int dirs = 0;
public int files = 0;
public String path = "";
}
public static void main(String[] args) {
System.out.println(new JSONObject(new FMList(), new String[] { "dirs", "files" }).toString());
}
}
Produces: {"files":0,"dirs":0}
Changing to private class produces: {}
The JSONObject expects to use the getters but your classes are only using public variables instead of the getters:
JSONObject(java.lang.Object bean)
Construct a JSONObject from an Object using bean getters.
http://www.json.org/javadoc/org/json/JSONObject.html#JSONObject(java.lang.Object)
Understood that you are looking specifically for serialization using the org.json package, but if you are open to other libs, Xstream is really easy to use for XML-related tasks, such as JSON serialization. Here is a JSON tutorial: http://x-stream.github.io/json-tutorial.html.

Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap

Using Jersey I'm defining a service like:
#Path("/studentIds")
public void writeList(JsonArray<Long> studentIds){
//iterate over studentIds and save them
}
Where JsonArray is:
public class JsonArray<T> extends ArrayList<T> {
public JsonArray(String v) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new MappingJsonFactory());
TypeReference<ArrayList<T>> typeRef = new TypeReference<ArrayList<T>>() {};
ArrayList<T> list = objectMapper.readValue(v, typeRef);
for (T x : list) {
this.add((T) x);
}
}
}
This works just fine, but when I do something more complicated:
#Path("/studentIds")
public void writeList(JsonArray<TypeIdentifier> studentIds){
//iterate over studentIds and save them by type
}
Where the Bean is a simple POJO such as
public class TypeIdentifier {
private String type;
private Long id;
//getters/setters
}
The whole thing breaks horribly. It converts everything to LinkedHashMap instead of the actual object. I can get it to work if I manually create a class like:
public class JsonArrayTypeIdentifier extends ArrayList<TypeIdentifier> {
public JsonArrayTypeIdentifier(String v) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new MappingJsonFactory());
TypeReference<ArrayList<TypeIdentifier>> typeRef = new TypeReference<ArrayList<TypeIdentifier>>(){};
ArrayList<TypeIdentifier> list = objectMapper.readValue(v, typeRef);
for(TypeIdentifier x : list){
this.add((TypeIdentifier) x);
}
}
}
But I'm trying to keep this nice and generic without adding extra classes all over. Any leads on why this is happening with the generic version only?
First of all, it works with Longs because that is sort of native type, and as such default binding for JSON integral numbers.
But as to why generic type information is not properly passed: this is most likely due to problems with the way JAX-RS API passes type to MessageBodyReaders and MessageBodyWriters -- passing java.lang.reflect.Type is not (unfortunately!) enough to pass actual generic declarations (for more info on this, read this blog entry).
One easy work-around is to create helper types like:
class MyTypeIdentifierArray extends JsonArray<TypeIdentifier> { }
and use that type -- things will "just work", since super-type generic information is always retained.

Categories

Resources