I am using Play Framework 1.2.4 with Java and using JPA to persist my database objects. I have several Model classes to be rendered as JSON. But the problem is I would like to customize these JSON responses and simplify the objects just before rendering as JSON.
For instance, assume that I have an object named ComplexClass and having properties id, name, property1,...,propertyN. In JSON response I would like to render only id and name fields.
What is the most elegant way of doing this? Writing custom binder objects or is there simple JSON mapping such as using a template?
Play Framework 1.2.4 directly depends on the gson library so you could use that to render your JSON strings. All you have to do is use gson's #Expose annotation. So in your example, you would mark the fields you want in your JSON string like this:
public class ComplexClass {
#Expose
public Long id;
#Expose
public String name;
...
}
Then in your controller, you would just do this:
public static void someActionMethod() {
// get an instance of your ComplexClass here
ComplexClass complex = ...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
String json = gson.toJson(complex);
renderJson(json);
}
See documentation here.
If ComplexClass is actually a play.db.jpa.Model and therefore the id field is abstracted away in a parent class and you can't put the #Expose annotation on it, then you could create your own ExclusionStrategy that skips fields that aren't annotated with #Expose and are not called id. So something like this (pseudo-code):
public final class ComplexClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes attributes) {
if (name of field is "id") return false;
if (field is annotated with #Expose) return false;
return true;
}
Then the controller would altered slightly to look like this:
GsonBuilder builder = new GsonBuilder();
ComplexClassExclusionStrategy strategy = new ComplexClassExclusionStrategy();
builder.setExclusionStrategies(strategy);
Gson gson = builder.create();
String json = gson.toJson(complex);
renderJson(json);
Use FlexJSON, it's really easy. It allows you to create JSONSerializers which can include/exclude the fields you want.
Check out this article for some examples of using it with Play! Framework.
Here's a simple example:
public ComplexClass {
public Long id;
public String name;
// And lots of other fields you don't want
public String toJsonString() {
// Include id & name, exclude all others.
JSONSerializer ser = new JSONSerializer().include(
"id",
"name",
).exclude("*");
return ser.serialize(this);
}
}
You can add it to your dependencies.yml like so:
require:
- play
- net.sf.flexjson -> flexjson 2.1
What I usually do is write an interface for models that implements a toJSONString() method so that I can call renderJSON(someModel.toJSONString()) in the controller.
Link to official website
EDIT: Extra example for lists/collections
Ok, when you start serializing list you might get some unexpected results. This is because the order of evaluation is important. The first include() or exclude() takes precedence over the following ones.
Here's an example of serializing the childs of a parent entity (OneToMany relation).
JSONSerializer ser = new JSONSerializer();
// Exclude these standard fields from childs
ser.exclude(
"*.persistent",
"*.class",
"*.entityId"
);
// Include childs and all its other fields
ser.include(
"childs",
"childs.*"
);
// Exclude everything else
ser.exclude("*");
String data = ser.serialize(parent);
The * is a wildcard by the way. This piece of documentation explains it perfectly:
An exclude of *.class will match to any path depth. So if flexjson is serializing the field with path of "foo.bar.class" the * in *.class will match foo.bar.
Related
I have an annotated entity object with custom table and field names which i use with Spring Data JDBC (not JPA). Smth like:
#Data
#Table("custom_record_table")
public class Record {
#Id
#Column("id_field")
Long id;
String name;
#Column("name_short")
String shortName;
}
I'd like to get a map of properties to fields. Smth like:
{"id":"id_field","name":"name","shortName":"name_short"}
What's the proper way to get it?
For context: I plan to use this map to construct queries to load many-to-one refs along with main table. Now I get this map with plain reflections API scanning for fields and their annotations. It works, but I am feeling like inventing a bicycle...
What you are looking for is the JdbcMappingContext. It should be available as a bean, so you can simply autowire it in your application.
JdbcMappingContext mappingContext // ... autowired
Map<String, String> propToCols = new HashMap<>();
mappingContext.getRequiredPersistentEntity(Record.class).forEach(
rpp -> propToCols.put(rpp.getName(), rpp.getColumnName().getReference()
);
I wrote this without IDE so it will contain mistakes.
There are also special things to consider when you have references and stuff, but this should get you started.
you want to convert your objects to JSON. they are different libraries for it. people prefer Jackson library the most
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(object);
What if you create a static function inside Record?
#Data
#Table("custom_record_table")
public class Record
{
#Id
#Column("id_field")
Long id;
String name;
#Column("name_short")
String shortName;
static Map<String, String> getMap()
{
return Map.ofEntries (
Map.entry("id", "id_field"),
Map.entry("name", "name"),
Map.entry("shortName","name_short")
);
}
/* returning map would look like:
{"id":"id_field","name":"name","shortName":"name_short"} */
}
Note that this would get you a Map, in which you can retrieve the field values by the field keys you know.
map.get("id") will return id_field, and so on. This may be useful when you need to know which is the name that references the fields.
BUT, if you just want an String representation, even a JSON, it would be best to create a JSON from those fields.
Anyway, if I understood correctly, you want a Map object, so the first solution should do it.
I'm trying to figure out a way to selectively de-serialize specific fields from flickr.
{"photos":{"page":1,"pages":10,"perpage":100,"total":1000,"photo":[{"id":"","owner":"","secret":"","server":"","farm":,"title":"","ispublic":,"isfriend":,"isfamily":0,"url_s":"","height_s":"","width_s":""},...]}
I receive an object that contains two lists (photos and photo) and i would like to model in Java only the id, url_s and title from photo.
I figured out that I can create my java module with #expose annotation for the fields i'm interested in and than use
builder.excludeFieldsWithoutExposeAnnotation();
this way I'll control which fields get deserialized from photo (still not sure it's gonne work that way), but what about photos?
My questions are:
Is there a way to ignore that list? do I have to model a Java class that contains two lists (with their respective fields) just to grab what I need from the second list?
expanding upon the former question, if my module class for photo is:
public class GalleryItem {
#Expose()
private String mCaption;
#Expose()
private String mId;
#Expose()
private String mUrl;
}
can i call gson only on the part i need?
Type galleryListType = new TypeToken<ArrayList<GalleryItems>> (){}.getType();
List<GalleryItems> itemsList = gson.fromJson(jsonString, galleryListType);
can I somehow use setExclusionStrategies to skip the Photos list?
gsonBuilder.setExclusionStrategies(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
**return f.getName().contains("photos")**;
}
#Override
public boolean shouldSkipClass(Class<?> incomingClass) {
return ;
}
});
I've already implemented a solution using raw JSONObject/JSONArray but I'm curious regarding using GSON for the task at hand.
Thanks in advance!
Gson provides deserialisation on Java fields. If you mark as a transient your variable, Gson will not serialised to JSON. Gson also provides finer deserialisation control via annotations: #Expose(deserialize = false/true)
The last option would be writing your custom JsonDeserializer<T>
Is it possible to dynamically create a GraphQL schema ?
We store the data in mongoDB and there is a possibility of new fields getting added. We do not want any code change to happen for this newly added field in the mongoDB document.
Is there any way we can generate the schema dynamically ?
Schema is defined in code, but for java(schema as pojo), when new
attribute is added, you have to update and recompile code, then
archive and deploy the jar again. Any way to generate schema by the
data instead of pre-define it?
Currently we are using java related projects (graphql-java, graphql-java-annotations) for GraphQL development.
You could use graphql-spqr, it allows you auto-generate a schema based on your service classes. In your case, it would look like this:
public class Pojo {
private Long id;
private String name;
// whatever Ext is, any (complex) object would work fine
private List<Ext> exts;
}
public class Ext {
public String something;
public String somethingElse;
}
Presumably, you have a service class containing your business logic:
public class PojoService {
//this could also return List<Pojo> or whatever is applicable
#GraphQLQuery(name = "pojo")
public Pojo getPojo() {...}
}
To expose this service, you'd just do the following:
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withOperationsFromSingleton(new PojoService())
.generate();
You could then fire a query such as:
query test {
pojo {
id
name
exts {
something
somethingElse
} } }
No need for strange wrappers or custom code of any kind, nor sacrificing type safety. Works with generics, dependency injection, or any other jazz you may have in your project.
Full disclosure: I'm the author of graphql-spqr.
After some days' investigation. I found it is hard to generate schema dynamically in Java (or cost is so high).
Well, from another way. I think we can use Map as a compromised way to accomplish that.
POJO/Entity
public class POJO{
#GraphQLField
private Long id;
#GraphQLField
private String name;
// ...
#GraphQLField
private GMap exts;
}
GMap is a customized Map (Because Map/HashMap is a JDK inner class which could not make as GraphQL Schema but only extend).
GMap
public class GMap extends HashMap<String, String> {
#GraphQLField
public String get(#GraphQLName("key") String key) {
return super.get(key);
}
}
Retrieve data from Client
// query script
query test
{
your_method
{
id
name
exts {
get(key: "ext") // Add a extended attribute someday
}
}
}
// result
{
"errors":[],
"data":
{
"list":
[
{"id":1, name: "name1", exts: {"get": "ext1"}},
{"id":2, name: "name2", exts: {"get": "ext2"}}
]
}
}
I am trying to parse this JSON which is coming as the response to a REST API call. Can you please help me parsing it as key value pairs?
The object names are not present. There is nesting as well. There seems to be no new line between records.
The aim is to extract this data and load it into a database.
[
{
"cc_emails":["feedback#xyz.com"],
"fwd_emails":[],
"reply_cc_emails":["feedback#xyz.com"],
"fr_escalated":false,
"spam":false,
"email_config_id":6000038087,
"group_id":6000110481,
"priority":1,
"requester_id":6010410791,
"responder_id":6002817857,
"source":1,
"company_id":null,
"status":2,
"subject":"fare",
"to_emails":["feedback#xyz.com"],
"product_id":null,
"id":45043,
"type":null,
"due_by":"2016-03-12T08:58:02Z",
"fr_due_by":"2016-03-08T08:58:02Z",
"is_escalated":false,
"description":"Dear xyze Team,\r\n\r\nWhy r u increased fair again and againasas0mail.gmail.com</a>.<br>\n",
"custom_fields":
{
"category":null,
"issue":null,
"route_id":null,
"phone_number":null,
"department":null,
"booking_id":null
},
"created_at":"2016-03-07T08:58:02Z",
"updated_at":"2016-03-07T08:58:03Z",
// ...... repeat
}
]
The best way to do this would be to use http://www.jsonschema2pojo.org/
Enter your json there
Change source type to JSON
set the correct class name and package.
The resulting pojo can be directly mapped from the json
If you are using resttemplate to hit the api then you can use getForObject to automatically set the pojo from the output.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java.lang.String-java.lang.Class-java.lang.Object...-
Using gson you can do this quite simply.
Do a class to match the fields in the json something like:
public class Example {
private List<String> cc_emails;
private List<String> fwd_emails;
private List<String> reply_cc_emails;
private Boolean fr_escalated;
private Boolean spam;
private Integer email_config_id;
...
private CustomFields custom_fields;
private String created_at;
private String updated_at;
}
Then you need to do another to map the custom fields
public class CustomFields {
private String category;
...
}
And using json you can parse it like this:
Type type = new TypeToken<Collection<Example>>(){}.getType();
new Gson().fromJson(json,type);
You have to exaplain to Gson it's a list, if it was a single object it would be this:
new Gson().fromJson(json,Example.class);
This is the aproach I usually take, also in the dates java.sql.Timestamp class might also parse it, you would need to try it though.
You can use Gson (https://github.com/google/gson) or Jackson (https://github.com/FasterXML/jackson) and deserialize it to a Map.
I have a Java web service with a Jersey REST endpoint that returns a list of Restaurant POJOs as JSON objects (see Restaurant class below). The endpoint looks like this
/api/restaurants
and returns all the data tied to the Restaurant class. However, I want to add another, more lean endpoint that looks like this
/api/restaurants/name
which returns only the id and name of the Restaurant POJO for all restaurants. Is there a nice way to do this in Jersey out of the box (e.g. specify the fields you want from a POJO for specific endpoints)?
The corresponding POJO looks something like this:
#XmlRootElement
public class Restaurant {
// Members
private Long id;
private String name;
private List<Menu> menus;
...
// Constructors
public Restaurant() {}
...
// Getters and setters
...
}
If you need anything else, please don't hesitate to let me know! Thanks!
Yes, Jersey has support for selecting the elements that are included in serialized XML/JSON. Take a look at the entity filtering section of the manual.
Essentially, you annotate particular #XmlElements in your POJO with custom annotations. In your REST resource, you pass the annotation to Jersey when you build the Response.
Note that this only works if you use EclipseLink MOXy as your JAXB provider.
First of all, I am guessing that your api is going to be
/api/restaurants/{restaurantId}/name
and not
/api/restaurants/name
And in regards to your question of jersey having this feature out the box, I am not certain about it. Although, this is a much easier way to handle this.
Inside your POJO you can do something like this:
public class Restaurant {
// Members
private Long id;
private String name;
private List<Menu> menus;
...
// Constructors
public Restaurant() {}
...
// Getters and setters
...
// For getting only id and name
public Map getIdAndName()
{
Map<Object, Object> map = new HashMap<>();
map.put("id", this.id);
map.put("name", this.name);
return map;
}
// For getting just a list of menu and name
public Map getNameAndMenu()
{
Map<Object, Object> map = new HashMap<>();
map.put("menus", this.menus);
map.put("name", this.name);
return map;
}
And in your service class you can pretty much use something like this
#Path("/api/restaurants/{restaurantId}/name")
#Produces("application/json")
public String getRestaurantName(#PathParam("restaurantId") String restaurantId)
{
// GET RESTAURANT
Restaurant restaurant = getRestaurant(restaurantId);
Gson gson = new Gson();
// CONVERT TO JSON AND RETURN (or let jersey do that serializable, whichever way is preferable to you.
return gson.toJson(restaurant.getIdAndName());
}
Hope this helps!