JSON: is it better to determine the type of an object inside or outside an object?

Context

We are creating the JSON API for websites (HTML + JS) and mobile (iOS / Android / Windows).

The server needs to send data with a basic structure and a variable structure. In our example, the basic structure includes "name" and "description", the structure of variables is called "template" and has different fields depending on its type. We have calculated at least three ways to write it (maybe more):

A: type of structure variable defined outside the object

{ "id": "A001", "name": "My First Game", ..., "template_type": "BATTLE", "template": { ... } } 

In this case, the client should look at "template_type" to determine how to parse the "template". The template object itself is not self-sufficient to know what it is.

B: variable structure type defined inside an object

 { "id": "A001", "name": "My First Game", ..., "template": { "type": "BATTLE", ... } } 

In this case, the client should look at the “type” inside the “template” to determine how to parse the “template”. The "template" object itself is self-sufficient to know what it is.

C: variable structure type defined by object key

 { "id": "A001", "name": "My First Game", ..., "template_battle": { ... } } 

In this case, the client should look at all the keys ("template_battle", "template_puzzle", ...) to determine what type of game we have. The template_battle object itself is self-sufficient to know what it is, because it will always be of the BATTLE type.

Question

Any recommendation that the JSON solution is the most customer and website friendly for analysis and use? (you can suggest other solutions)

+5
source share
7 answers

Personally, I would put the type in the template itself for a simple reason, that is, encapsulation. Imagine that you want to separate the creation of a template and an external object (remember the separation of problems and the principle of shared responsibility ( https://en.wikipedia.org/wiki/Single_responsibility_principle )). If the type is on an external object, you should always specify the type of the template so that it can be created. This is an opportunity, but it increases connectivity and breaks encapsulation.

For further reading, I recommend https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) to get started.

+7
source

I would recommend going with option A for two simple reasons.

A> B as separate data and types:

Separates type information from the data itself. By doing this, you would not have name conflicts if you said that the template_type property is associated with it. You could make it easier to list all the properties and set them to your custom object without having to have a special case to ignore the type property.

A> C, because less work:

Parsing the main line is more work. To find the template_* key first, you will need to list the properties and iterate over them to find the one you need.

Ultimately, I think option A will give you the easiest way to parse and use data.

+3
source

Approach B will be much better IMHO. This is simply because it provides a general approach for the user to access the attributes of the template without touching its type . Thus, the user can simply write his program for a common template, which includes the type as an attribute of himself.

For example, imagine you have an object type named Template that displays the json template definition for a Java object.

 Class Template{ String type; String attribute1; String attribute2; ...... ...... } 

Using approach B, you can directly map the json definition of this template to the template object. (In this case, it is a Java object, but, of course, the concept works for any other programming language).

The user does not need to have prior knowledge of the template type before accessing the template definition. That is why this is called a more general approach.

+2
source

I would prefer your option B over A and C.

However, you can 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 can improve perception in everyday use.

+2
source

I would prefer B over others because it will share problems / data.

Since here, if you want to process only the template data, you can easily extract the template data in one step in case B (for example, Obj.template). But this is not a simple case of A.

And also, if you add several types of templates in the future, then if you want to extract template data, it will be directly in case B (for example, Obj.template), but in case C , you need to write the code as shown 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]. 

Therefore, I prefer B over others.

+2
source

I would recommend using static and dynamic structure in two different collections.

As below

Static structure and using a dynamic field as an array and passing a unique identifier or field, which, in your opinion, can be unique.

 { "id": "A001", "name": "My First Game", "description" : "GGWP noob", ..., "template": ['temp1','temp2','temp3','temp4'], } 

Dynamic structure . In the dynamic structure, you can transfer the remaining fields to another api, since the main functionality , such as search, autocomplete, may depend on them. Similarly, you can easily reference the parent api.

  { "id" : "temp1", "type": "BATTLE", ... //other features } 

It also allows faster searching , indexing and good compression . Instead of going through the entire single JSON api to find the appropriate tags, a dynamic structure helps reduce overhead .

There are many other main uses for this approach, but I have just mentioned a few of them that I think will help you design this way.

+1
source

B: easier to use standalone json node

As the other answers say, I would go to B because of encapsulation, but I will give one more pragmatic reason: think about what the general process that you develop yourself will do, or if you use the library: I will use Jackson " (maybe it can be used on Android).

  • If the type is outside the JsonNode that you are analyzing, you need to specify in the deserializer for each property where the type is located, if it is inside the same Node, you only specify where the type is "inside the object", and for many objects it can to be the same.
  • An additional argument, if you pass only the “battle” object, it does not have a container, so there are no external properties to indicate the type
  • Another argument, at least 1 JS library uses this method: ExtJS, see the "xtype" property in the 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 a 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 provides a solution for "gessing" 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 a specific Jackson, you can parse Node with a simple JsonNode in java.

Editing: I see that this may seem off topic as I give a technical solution, so I clarify that the argument here is not to use Jackson, it should show that regardless of the language of the solution and the library you choose, It is possible to use the “B” solution in an elegant way.

D: node encapsulation Another solution is the following:

  { "BATTLE":{ "aPropertyOfBattle":1 } } 

It may be easier to analyze: you get the name of the property, then you parse under the node using any tool (Gson or another ...) In Jackson, the only difference is that you use include = As.WRAPPER_OBJECT

The inconvenient thing in Javascript is less logical to use, since you have a useless Node in the middle of your structure.

Another Jackson Solution

This library as other options include = As....

  • As.WRAPPER_ARRAY
  • As.EXTERNAL_PROPERTY

    WRAPPER_ARRAY easier to parse, but I don't find it elegant (this is completely subjective):

     [ "BATTLE", { "aPropertyOfBattle":1 } ] 

    EXTERNAL_PROPERTY will be A. solution, but, as I said, you should specify it every time you use your variable, and not in your type class (I do not have consistency with me, because you can use the Battle object in another context, with another naming convention)

     @JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = "template_type") private ICustomType template; 

Remember once again, I am inspired by the functionality of Jackson, but it can be applied to every solution in every language.

+1
source

Source: https://habr.com/ru/post/1259144/


All Articles