Reputation: 38667
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):
{
"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.
{
"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.
{
"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.
Any recommendation on which JSON solution is the most client friendly for web and mobile to parse and use? (you can propose other solutions)
Upvotes: 7
Views: 1706
Reputation: 4067
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).
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.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.
Upvotes: 2
Reputation: 22199
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.
Upvotes: 1
Reputation: 60527
I would recommend going with option A, for 2 simple reasons.
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.
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.
Upvotes: 4
Reputation: 1051
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.
Upvotes: 2
Reputation: 5955
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.
Upvotes: 2
Reputation: 851
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.
Upvotes: 2
Reputation: 2257
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.
Upvotes: 7