How do I parse a generic class parameter from JSON in Scala? - java

I have a generic getter trait
trait Getter[A] {
def get: A
}
and I would like to parse JSON into a List of objects implementing this trait. Two such implementations:
case class CoalesceGetter[A](getters: List[Getter[String]]) extends Getter[A] {
override def get: A = getters.map(_.get).find(_ != null).orNull
}
case class AsnGetter(ipGetter: Getter[String]) extends Getter[Long] {
override def get: Long = 99L // dummy function
}
I would like to parse JSON into the correct Getter class based upon a property called function which corresponds to the class and type which corresponds to the generic type in the case of getters which need a generic (both properties are strings in the json blob I'm parsing). I've looked at custom serializers for json4s but don't see how to work with generics. Any help is appreciated!

First of all, I don't think it is a good idea to jsonify classes with type argument. I think it is a better design to define non-typed (case) classes that are direct equivalent of your json object, and use standard read/write json as provided by many libraries.
But then, to answer your question, I'd like to return another question: how would you do it "manually"?
I.e. how would you write and read different CoalesceGetter[A] with different A?
Here is a proposition: put the type arg in a json field:
"ofInt": {"type-arg":"Int", "getters":[ ... list of getters in json ...]},
"ofDouble":{"type-arg":"Double", "getters":[ ... list of getters in json ...]}
Now, if you'd write the reader, how would you instantiate the 2 ofInt and ofDouble, knowing the type-arg "Int" and "Double" (which are string!).
I see 2 solutions:
1) Either you have a hard-coded map of arg-type string => actual scala type
argType match{
case "Int" => new CoalesceGetter[Int](...)
case "Double" => new CoalesceGetter[Double](...)
}
2) Or you store and read a generalized type as string value in the arg-type string, such as the java Class.forName (see [https://stackoverflow.com/a/7495850/1206998] for example). But this is a really really bad idea IMHO.
(note: if you want to serialize any object just to reload it later or on another computer, don't use json but dedicated serialization such as the Java Serialization or kryo that is used by spark)

Related

GraphQL, how to return type of byte[]

I have thumbnails saved in my database as a byte array. I can't seem to workout how to return these to the frontend clients via GraphQL.
In a standard REST approach I just send a POJO back with the bytes and I can easily render that out.
However trying to return a byte[] is throwing
Unable to match type definition (ListType{type=NonNullType{type=TypeName{name='Byte'}}}) with java type (class java.lang.Byte): Java class is not a List or generic type information was lost: class java.lang.Byte
The error is descriptive and tells me what's wrong, but I don't know how to solve that.
My thumbnail.graphqls looks like:
type Thumbnail {
id: ID!
resource: [Byte!]
}
And the thumbnail POJO
public class Thumbnail extends BaseEntity {
byte[] resource;
}
I'm using graphql-spring-boot-starter on the Java side to handle things, and I think it supports Byte out the box, so where have I gone wrong?
Very fresh to GraphQL so this could just be an obvious mistake.
Cheers,
You have to serialize it to one of the standard types.
If you want your byte array to look like a string such as "F3269AB2", or like an array of integers such as [1,2,3,4,5] its totally up to you.
You can achieve the serialization by writing a resolver for your entity, like that:
public class ThumbnailResolver extends GraphQLResolver<Thumbnail> {
public String resource(Thumbnail th) { ... }
//or List<Integer> resource(Thumbnail th) { ... }
//or whatever
}
The resolver have always priority over your entity. This means that if a resolver method with the correct name, parameters and return type is found in the resolver class, this will be called instead of the entity method. This way we can "override" entity methods, in order to return an other result, even a different type than the actual entity field. By using resolvers, we could also have access to application scoped services etc that an entity typically does not have.
After writing your resolver, don't forget to update your schema file to:
resource: String
#or resource:[Int]
#or whatever
Your schema should refere to the resolver type since this is what graphQL recieves. The actual entity type will become then irrelevant to graphQL.
As a plan B, you could implement a new Scalar. This would be like inventing a new basic type. This is also not that hard. You can see the already existing scalar types here and do something similar.
You can then name your new type ByteArray or something like that, declare it in your schema:
scalar ByteArray
and then use it.
I would go for the first solution though since it is easier and faster to implement.

Jackson Deserialize To Concrete Class Based On Type

I have what I believe should be a simple use case.
I would like to serialize a POJO with type metadata (preferably a simple name I come up with, not the fully qualified class/package name), and later have Jackson deserialize the JSON back into the concrete class it came from by using this metadata. There is no inheritance hierarchy among classes being serialized and deserialized.
My scenario is I have a service which accepts multiple file types. For each file uploaded, the client can retrieve JSON data whose structure and type depends on the file it came from. Thus when I retrieve JSON from the service, it's not known what the concrete class is to deserialize to. I would like Jackson to figure this out based on metadata which it supplies.
For example, I'd like to be able to do this:
String json = ... // get JSON from the service
Object obj = mapper.readValue(json, Object.class) // concrete class is not known
System.out.println(obj.getClass()) // I want this to be MyConcreteClass.class
There is no inheritance hierarchy among JSON types returned.
I don't want to reveal package names or other internal service
details/structure.
I have control over Jackson's serialization process
Relevant question: Can jackson determine root object type to deserialize to when json includes type property?
Thank you so much for your help!
This can be achieved using Jackson's JavaType:
String className = "class.name.from.json.service";
JavaType dtoType = TypeFactory.defaultInstance().constructFromCanonical(className);
Object dto = new ObjectMapper().readValue(InputStream, dtoType);
assert dto.getClass().equals(dtoType.getRawClass());

Instantiate generic based on property file setting

I'm building a data driven test system. I have done this before in XML but json is giving me some interesting issues.
For each request and response type json, I have a setting in my script where I specify a pojo type. This type is instantiated to a class object thats passed to jackson to marshal the json into a usable pojo. so its like this:
"responseType": "java.util.List",
eventually gets pumped to
Class<?> reponseType = null;
try {
if (d.shouldPass) {
reponseType = Class.forName(d.responseType);
}
} catch (ClassNotFoundException e) {
throw new RequestResponseTypeInvalid(testName);
}
and I have usable class info to use in jackson. My problem is I need to do this:
"responseType": "java.util.List<foo>",
otherwise complex json types parse as hashmaps instead of pojo's. I suppose I can get creative and put something in to go from hashmap to pojo if I need to but I was wondering if there was any straight forward way to do this.
I suppose another way is to implement a factory class where I could say list_foo in the property file and have the factory class map that to an actual class object. That wouldn't be very hard but not as easy as just using the property.
thanks
You can't do this in the way that you're hoping, I'm afraid. Generics are a compile-time thing only, and can't be used in this way at runtime, because of type erasure.
The best you could do would be to have some list_foo properties, and map these explicitly to List<Foo> and so in in your code. But you can't do it by reflection.

How to implement with AutoBean a list of different base types like String, Integer, etc.?

I want to create a request for JSON-RPC with three parameters - String, Integer and my own object. Request should look like this:
{"method":"MyMethod", "params":["text", 123, {"name": "any text", "num": 15}], "id":1}
Ideally, I would like to create an AutoBean like this (but it does not work):
interface JsonRpcRequest {
String getJsonrpc();
void setJsonrpc(String value);
String getMethod();
void setMethod(String value);
List<Object> getParams(); // ERROR: Type Object may not be used
void setParams(List<Object> params); // ERROR: Type Object may not be used
}
interface JsonRpcRequestFactory extends AutoBeanFactory {
AutoBean<JsonRpcRequest> jsonRpcRequest();
}
The problem is that the AutoBean framework does not allows the use of List<Object> inside interface.
Is there another way to create a list/array of elements of different based and non-based types?
No, you simply can't. AutoBean requires everything to be statically typed: no polymorphism, and no mixed-typed lists of maps.
You might be interested by RequestFactory's built-in support for JSON-RPC though.
Why do your params all need to be passed back in a list? Surely you're not going to do the same thing with a String, an Integer, and another Object! Just send them all back separately.
Further, you're not sending a custom Object over the JSON, you're sending the objid of that object... so just send the Integer id and let the server handle it.

Is there a way to create the bean class from a json response

Converting JSON to Java
The above question is with reference to what has been described on the above thread. There are so many API(s) which provide the flexibility to return responses either in XML or JSON. **I would like to know if there is a way to automatically construct the java bean corresponding to a JSON response. **
lets say you get an object like
[
{
"name":"Java 6 Greatest Hits",
"Author":"Jim Bob Jones",
"price":10.25
},
{
"name":"How to raise a goat",
"Author":"Sir Paxton",
"price":55.97
},
{
"name":"Snow - It is cold",
"Author":"Dr. White",
"price":9.99
}
]
And you want a class like
public class Book{
private String author;
private String name;
private Number price
}
with getters and setters
One option is to use a service like JSONGen, which will create that class. You need to use it first, and include the generated code in your project.
Another option could be dynamically generate the class using javassist or CGLib, but that class would be useless unless you use reflection to access its members, so even if it would be a class, it will behave like a really annoying Map. In no way will be better that simple using JSONObject
seems a simple Message Type Entity not meet you requirement ?
if you want convert a json to an existed and known java bean class,
many lib can do so, like
http://json-lib.sourceforge.net/apidocs/net/sf/json/class-use/JSONObject.html
JSONObject.toBean(JSONObject jsonObject, Class beanClass)
Creates a bean from a JSONObject, with a specific target class.
btw, if you are communicating with restful webservice, org.springframework.web.client.RestTemplate will help you get direct bean result
insteadof json.
if class does not exists, you need program with java reflect mechanism.
try use CGLIB ,http://cglib.sourceforge.net/, dynamic create some class like BeanMap. i wrote a simple sample,
but be ware, opearting class byte is hard and you may meet strange trouble with JVM . Strongly not encourage to do so.
public static BeanMap generateBean(JSONObject json) {
BeanGenerator generator = new BeanGenerator();
Iterator keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
Class keyClass = guessValueClass(value);
generator.addProperty(key.toString(), keyClass);
}
Object result = generator.create();
BeanMap bean = BeanMap.create(result);
keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
bean.put(key, value);
}
return bean;
}
/**
* TODO fix guess
*/
static Class guessValueClass(Object value) {
try {
Integer.parseInt(value.toString());
return Integer.class;
} catch (NumberFormatException e1) {
}
try {
Double.parseDouble(value.toString());
return Double.class;
} catch (NumberFormatException e1) {
}
return String.class;
}
I believe the main issue here is that the JSON response lacks type information and last time I checked :-) in Java you need to declare the type of a class property. So some heuristics will be needed to infer the type form the value in the JSON response.
For a related question here in SO have a look at: Generate Java class from JSON?
Yes check out http://flexjson.sourceforge.net
If you're wanting to generate Java classes from JSON, perhaps you could try Jackson. It provides a lot of JSON-related functionality, including the ability to generate bytecode from arbitrary JSON. See this blog post for details.
If you're using Jackson (the most popular library there), try
https://bitbucket.org/astav/jsontojava/wiki/Home
Its open source and anyone should be able to contribute.
Summary
A JsonToJava source class file generator that deduces the schema based on supplied sample json data and generates the necessary java data structures.
It encourages teams to think in Json first, before writing actual code.
Features
Can generate classes for an arbitrarily complex hierarchy (recursively)
Can read your existing Java classes and if it can deserialize into those structures, will do so
Will prompt for user input when ambiguous cases exist

Categories