I am a Scala newbie, extending someone else's code. The code uses the Play framework's JSON libraries. I am accessing objects of class Future[Option[A]] and Future[Option[List[B]]. The classes A and B each have their own JSON writes method, so each can return JSON as a response to a web request. I'm trying to combine these into a single JSON response that I can return as an HTTP response.
I thought creating a class which composes A and B into a single class would allow me to do this, something along these lines:
case class AAndB(a: Future[Option[A]], b: Future[Option[List[B]]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}
But that fails all over the place. A and B are both structured like this:
sealed trait A extends SuperClass {
val a1: String = "identifier"
}
case class SubA(a2: ClassA2) extends A {
override val a1: String = "sub identifier"
}
object SubA {
val writes = Writes[SubA] { aa =>
Json.obj(
"a1" -> aa.a1
"a2" -> aa.a2
)
}
}
Since B is accessed as a List, the expected output would be along these lines:
{
"a":{
"a1":"val1",
"a2":"val2"
},
"b":[
{
"b1":"val 3",
"b2":"val 4"
},
{
"b1":"val 5",
"b2":"val 6"
},
{
"b1":"val 7",
"b2":"val 8"
}
]
}
Your help is appreciated.
As #cchantep mentioned in the comments on your question, having Futures as part of a case class declaration is highly unusual - case classes are great for encapsulating immutable domain objects (i.e that don't change over time) but as soon as you involve a Future[T] you potentially have multiple outcomes:
The Future hasn't completed yet
The Future failed
The Future completed successfully, and contains a T instance
You don't want to tangle up this temporal stuff with the act of converting to JSON. For this reason you should model your wrapper class with the Futures removed:
case class AAndB(a: Option[A], b: Option[List[B]])
object AAndB {
implicit val implicitAAndBWrites = Json.writes[AAndB]
}
and instead use Scala/Play's very concise handling of them in your Controller class to access the contents of each. In the below example, assume the existence of injected service classes as follows:
class AService {
def findA(id:Int):Future[Option[A]] = ...
}
class BListService {
def findBs(id:Int):Option[Future[List[B]]] = ...
}
Here's what our controller method might look like:
def showCombinedJson(id:Int) = Action.async {
val fMaybeA = aService.findA(id)
val fMaybeBs = bService.findBs(id)
for {
maybeA <- fMaybeA
maybeBs <- fMaybeBs
} yield {
Ok(Json.toJson(AAndB(maybeA, maybeBs)))
}
}
So here we launch both the A- and B-queries in parallel (we have to do this outside the for-comprehension to achieve this parallelism). The yield block of the for-comprehension will be executed only if/when both the Futures complete successfully - at which point it is safe to access the contents within. Then it's a simple matter of building an instance of the wrapper class, converting to JSON and returning an Ok result to Play.
Note that the result of the yield block will itself be inside a Future (in this case it's a Future[Result]) so we use Play's Action.async Action builder to handle this - letting Play deal with all of the actual waiting-for-things-to-happen.
Context
We are building a JSON API for web (HTML+JS) and mobile (iOS/Android/Windows).
Server needs to send data with a base structure and a variable structure. In our example, the base structure includes "name" and "description", the variable structure is called "template" and have different fields depending on its type. We figured out at least three ways to write it (there may be more):
A: variable structure type defined outside the object
{
"id": "A001",
"name": "My First Game",
...,
"template_type": "BATTLE",
"template": {
...
}
}
In this scenario, the client should look at "template_type" in order to determine how to parse "template". The "template" object alone is not self-sufficient to know what it is.
B: variable structure type defined inside the object
{
"id": "A001",
"name": "My First Game",
...,
"template": {
"type": "BATTLE",
...
}
}
In this scenario, the client should look at "type" inside "template" in order to determine how to parse "template". The "template" object alone is self-sufficient to know what it is.
C: variable structure type defined by the key of the object
{
"id": "A001",
"name": "My First Game",
...,
"template_battle": {
...
}
}
In this scenario, the client should look at all keys ("template_battle", "template_puzzle", ...) in order to determine which type of game we have. The "template_battle" object alone is self-sufficient to know what it is, because it would always be the "BATTLE" type.
Question
Any recommendation on which JSON solution is the most client friendly for web and mobile to parse and use? (you can propose other solutions)
Personally, I would put the type on the template itself for a simple reason, that is encapsulation. Imagine you want to separate the creation of the template and the outside object (remember separation of concerns and the single responsibility principle (https://en.wikipedia.org/wiki/Single_responsibility_principle)). If the type is on the outside object, you will always have to specify the type of the template, to be able to create it. That's a possibility, but it increases coupling and violates encapsulation.
For further reading I recommend https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) for the beginning.
I would recommend going with option A, for 2 simple reasons.
A > B because separate data and types:
It separates the type information from the data itself. By doing this, you would not have naming conflicts if say you wanted a template_type property associated with it. You could potentially simplify it enumerate all the properties and set them on your custom object, without having to have a special case to ignore the type property.
A > C because less-work:
Parsing the key string is more work. To find the template_* key in the first place, you would need to enumerate the properties and loop over them to find the one you want.
Ultimately, I think option A will give you the easiest method of parsing and using the data.
The approach B would be much better IMHO. That is simply because, it provides a generic approach for user to access the template's attributes without concerning about its type. In this manner, user can simply write his program for a generic template which include the type as an attribute of itself.
For example, imagine you have a object type named Template which maps
the json definition of a template to a Java object.
Class Template{
String type;
String attribute1;
String attribute2;
......
......
}
By using approach B, you can directly map the json definition of that
template, to above template object.(In this case, it is a Java object
but of course the concept works for any other programming language).
User does not need to have an prior knowledge of template type, before
accessing the template's definition. That's why it is said to be a
more generic approach.
I'd prefer your B variant over A and C.
However, you might also consider a structure like this:
{
"longDesc": "The Long description and other(?)necessary hints here",
"type": "template",
"ID": {
"A001": {
"name": "My First Game",
"type": "BATTLE"
/*more data here*/
},
"A002": {
"name": "My 2nd Game",
"type": "STRATEGY"
/*more data here*/
}
}
};
It might give a better feel in everyday use.
I would prefer B over the others, because it will separate the concerns/data.
Since here if you want to process only template data you can easily extract template data in one step in case of B (E.g Obj.template) But it is not easy case of A.
And also, If you add multiple types of templates in future then if you want to extract template data, it is straight forward in case B(E.g Obj.template), But in case of C , you need to write code like below,
if(template_type='temp1'){
template=Obj["template_tep1"]
}
if(template_type='temp1'){
template=Obj["template_tep1"]
}
if(template_type='temp1'){
template=Obj["template_tep1"]
}
or
you need to write code like template=Obj["template"+Obj.template_type].
So I will prefer B over others.
B: It is easier to use a standalone json node
As other answers says, I would go for B for encapsulation reason, but I will give another pragmatic reason: Think of what would do a generic process that you develop yourself, or if you use a library: I will use "Jackson" (it seem possible to use it on Android).
If the type is outside the JsonNode you parse, you will have to specify in your deserializer for each property where the type is, if it is inside the same Node, you specify only where the type is "inside the object", and it can be the same for many objets.
An additional argument, if you pass the "Battle" object only, it doens't have a container, so no external properties to specify the type
Other argument, at least 1 JS library use this technique: ExtJS,see the "xtype" property in documentation http://docs.sencha.com/extjs/5.0.1/guides/getting_started/getting_started.html
So here is the Node you want to parse with the good type:
{
"type": "BATTLE",
"aPropertyOfBattle":1
}
here is the jackson code for this
#JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes ({
#JsonSubTypes.Type (Battle.class),
//...all your types
})
public interface ICustomType {}
#JsonTypeName("BATTLE")
public class Battle implements ICustomType{
int aPropertyOfBattle;
// getters/setters...
}
jackson provide a solution for "gessing" the type:
Full working code :
#JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes ({
#JsonSubTypes.Type (Battle.class),
//...all your types
})
public interface ICustomType {}
#JsonTypeName("BATTLE")
public class Battle implements ICustomType{
int aPropertyOfBattle;
// getters setters...
}
public class BattleContainer {
private ICustomType template;
private String id;
private String name;
// getters/setters
}
public class BattleTest {
#Test
public void testBattle() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
ICustomType battle = objectMapper.readValue("{'type': 'BATTLE','aPropertyOfBattle':1}".replace('\'','"'),Battle.class );
Assert.assertTrue("Instance of battle",battle instanceof Battle);
Assert.assertEquals(((Battle)battle).getaPropertyOfBattle(),1);
}
#Test
public void testBattleContainer() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
BattleContainer battleContainer = objectMapper.readValue("{'id': 'A001','name': 'My First Game','template': {'type': 'BATTLE', 'aPropertyOfBattle':1}}"
.replace('\'','"'),BattleContainer.class );
Assert.assertTrue("Instance of battle",battleContainer.getTemplate() instanceof Battle);
Assert.assertEquals(((Battle)battleContainer.getTemplate()).getaPropertyOfBattle(),1);
}
}
Note that this is not jackson specific, you can parse the node using simple JsonNode in java.
Edit: I am seeing that it may seem out of subject since I give a technical solution, so I precise that the argument here is not to use Jackson, it is to show that whatever the solution language and library you choose, it is possible to use the "B" solution in an elegant way.
D: An encapsulating node
An other solution is this one:
{
"BATTLE":{
"aPropertyOfBattle":1
}
}
It may be easier to parse: you get the property name, then you parse the sub-node using any tool (Gson or other...)
In jackson, the only difference is that you use include = As.WRAPPER_OBJECT
The inconvenient is that it is less logical to use in Javascript since you have a useless node in the middle of your structure.
Other solution of Jackson
This library as other options behind include = As....
As.WRAPPER_ARRAY
As.EXTERNAL_PROPERTY
WRAPPER_ARRAY is easier to parse too, but i don't find it elegant (it is totally subjective) :
[
"BATTLE",
{
"aPropertyOfBattle":1
}
]
EXTERNAL_PROPERTY would be the A. solution, but as I said, you must specify it every time you use your variable and not in your type class (it lacks of coherence to me, because you can use the "Battle" object in different context, with different type naming convention)
#JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = "template_type")
private ICustomType template;
Note again, I am inspired of jackson functionality, but it can be applied for every solution in every language.
I would recommend you to use the static and the dynamic structure in two different collections
As shown below
Static Structure and using the dynamic field as an array and by passing the unique id or the field which you think may be unique.
{
"id": "A001",
"name": "My First Game",
"description" : "GGWP noob",
...,
"template": ['temp1','temp2','temp3','temp4'],
}
Dynamic Structure. In the dynamic structure you can pass the rest of the fields into a different api,since the major functionality like searching,autocomplete might be dependant on them. Similarly it can also be referenced by the parent api easily.
{
"id" : "temp1",
"type": "BATTLE",
... //other features
}
This also allows faster searching,indexing and good compression.Rather than traversing through the whole single JSON api to search for the relevant tags, the dynamic structure helps in reducing the overheads.
There are many other major uses of this approach but I have mentioned only a few of them which i think would help you design in such a way.
is it possible to make use of the concept of shared references with JSON as output mode? I read this article http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html), but changing the #Produces on my JAX-RS to JSON forces an endless loop. Basically i want to reduce an object to it`s id:
public class Foo {
private long id;
private String someText;
private Bar bar;
}
I want this to bind instances of this like so:
{
"id": 1234,
"someText": "lorem",
"bar_id": 9876
}
This is what i want to avoid:
{
"id": 1234,
"someText": "lorem",
"bar": {
"id": 9876,
"anotherText": "ipsum"
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
With MOXy as your JSON-binding provider the shared references post you referenced (from my blog) will work for JSON just as it does for XML. Since you are using JAX-RS below is an example of configuring MOXy in that environment:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
When using #XmlID/#XmlIDREF it is expected that the object being reference by its ID also exist in the document. Since this isn't your use case you would be better off using an XmlAdapter. The XmlAdapter would have a signature similar to:
public class BarAdapter extends XmlAdapter<Integer, Bar> {
...
}
In the XmlAdapter you would be responsible for returning the id from the instance of Bar during the marshal, and for returning an instance of Bar based on the id during an unmarshal.
I have a Spring controller method that is correctly returning a collection of objects in the JSON to the client:
#RequestMapping(value="/admin/rawreads/unknowntags", method=RequestMethod.GET)
public #ResponseBody Collection<RawRead> getRawReadsWithUnknownTag() {
....
}
I'm trying to integrate a jQuery addon/plugin called DataTables. DataTables can handle the asynchronous request to fetch data and populate a table in the page for me, however it requires the data to be return in an array with a specific name "aaData" rather than the current format of :
[{"oid":149452,"date":1233076733000,"readerId":"811210008","recordNo":226,"tagCode":"0103A003C0","reader":null,"guard":null,"checkpoint":null},{"oid":149453,"date":1233076734000,"readerId":"8112100 .... etc]
Suggested return format for DataTables from the DataTable docs:
{
"sEcho": 1,
"iTotalRecords": "n",
"iTotalDisplayRecords": "n",
"aaData": [
[object1][object2].... etc
]
}
How can I change my Java method to return data in the format DataTables needs - a named array?
EDIT:
Tried putting vars into a HashMap serverside, gets a bit closer to what I need:
{"iTotalDisplayRecords":3050,"iTotalRecords":3050,"aaData":
[{"oid":149452,"date":1233076733000, ...<snip>},{...}]
}
Make a separate class in the correct format for data tables, having fields for sEcho, itotalrecords, ittotaldisplayrecords and then an array called aaData.
Then return this class via jason and that is then in your suggested format.
class TableDTO
{
int sEcho;
int iTotalRecords;
int itotalDisplayRecords;
array[] aaData;
}
Obviously assign data to these fields and proper accessors etc!
You must write one more TotalDisplayRecords class and it must incluse array oid class. After that, you return TotalDisplayRecords object.
I have a JSON object which I don't have control of and want to map it to a Java object which is pre-created.
There is one attribute in the JSON object which can be a URL or it could be a JSONArray.
Class SomeClass {
private URL items;
public URL getURL() {
return items;
}
public void setURL(URL url) {
this.items = url;
}
}
Below is the JSON:
Case A:
{
...
items: http://someurl.abc.com/linktoitems,
...
}
OR
Case B
{
...
items: [
{ "id": id1, "name": name1 },
{ "id": id2, "name": name2 }
]
...
}
If i create the POJO to map for Case A, Case B fails and vice versa. In short, is there a way to map the JSON attribute to the POJO field with different data types? In that case I will create two separate fields in the POJO named,
private URL itemLink;
private Item[] itemList;
It depends on exact details, but if what you are asking is if it is possible to map either JSON String or JSON array into a Java property, yes this can be done.
Obvious way would be to define a custom deserializer which handles both kinds of JSON input.
But it is also possible to define Java type in such a way that it can be constructed both by setting properties (which works from JSON Object) and have a single-String-arg constructor or static single-String-arg factory method marked with #JsonCreator.
Yet another possibility is to use an intermediate type that can deserialized from any JSON: both java.lang.Object and JsonNode ("JSON tree") instances can be created from any JSON. From this value you would need to do manual conversion; most likely in setter, like so:
public void setItems(JsonNode treeRoot) { .... }
What will not work, however, is defining two properties with the same name.
One thing I don't quite follow is how you would convert from List to URL though. So maybe you actually do need two separate internal fields; and setter would just assign to one of those (and getter would return value of just one).