I am trying to transform a JSON using Jolt transformation looking for some input here.
I am trying to filter by the inner value of the attribute.
My goal is to get an array that contains only the items with the typeName 'xx'. But not all the item object, only some of the fields
{
"id": "11_1",
"action": "add",
"payment": {
"paied": true,
"coin": "dollar"
},
"type": {
"id": "11_1_xx",
"typeName": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
}
}
}
Here is my input and expected output:
Input:
{
"id": 11,
"item": [
{
"id": "11_1",
"action": "add",
"payment": {
"paied": true,
"coin": "dollar"
},
"type": {
"id": "11_1_xx",
"typeName": "xx",
"typeGroup": "xx",
"typeValue": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
},
"reason": {
"id": "123",
"name": "xx"
}
},
"item": [
{
"id": "11_1_1",
"action": "add",
"payment": {
"paied": true,
"coin": "dollar"
},
"type": {
"id": "11_1_1_zz",
"typeName": "zz",
"typeGroup": "zz",
"typeValue": "zz"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
},
"reason": {
"id": "123",
"name": "xx"
}
},
"item": [
{
"id": "11_1_1_1",
"action": "add",
"payment": {
"paied": true,
"coin": "NIS"
},
"type": {
"id": "11_1_1_1_xx",
"typeName": "xx",
"typeGroup": "xx",
"typeValue": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
},
"reason": {
"id": "123",
"name": "xx"
}
}
}
]
},
{
"id": "11_1_2",
"action": "add",
"payment": {
"paied": false,
"coin": "dollar"
},
"type": {
"id": "11_1_2_xx",
"typeName": "xx",
"typeGroup": "xx",
"typeValue": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
},
"reason": {
"id": "123",
"name": "xx"
}
},
"item": [
{
"id": "11_1_2_1",
"action": "add",
"payment": {
"paied": false,
"coin": "NIS"
},
"type": {
"id": "11_1_2_1_zz",
"typeName": "zz",
"typeGroup": "zz",
"typeValue": "zz"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
},
"reason": {
"id": "123",
"name": "xx"
}
}
}
]
}
]
}
]
}
Expected output:
[
{
"id": "11_1",
"action": "add",
"payment": {
"paied": true,
"coin": "dollar"
},
"type": {
"id": "11_1_xx",
"typeName": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
}
}
},
{
"id": "11_1_1_1",
"action": "add",
"payment": {
"paied": true,
"coin": "NIS"
},
"type": {
"id": "11_1_1_1_xx",
"typeName": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
}
}
},
{
"id": "11_1_2",
"action": "add",
"payment": {
"paied": false,
"coin": "dollar"
},
"type": {
"id": "11_1_2_xx",
"typeName": "xx"
},
"details": {
"place": {
"id": "123",
"name": "xx"
},
"status": {
"id": "123",
"name": "xx"
}
}
}
]
Can you please help me to write a simple spec that will do this?
You can use the following explained spec
[
{
// Determine all key-value pairs partitioned under the main value and type.typeValue combinations
"operation": "shift",
"spec": {
"item": {
"*": {
"item": {
"*": {
"item": {
"*": {
"type": {
"#(1,id)": "#(2,id).#(1,typeName).id", //traverse } character twice in order to reach the main id of the object
"#(1,action)": "#(2,id).#(1,typeName).action",
"*": "#(2,id).#(1,typeName).&1.&" // &1 replicates "type" key, & does the leaf value
},
"*": "#(1,id).#(1,type.typeName).&"
}
},
"type": {
"#(1,id)": "#(2,id).#(1,typeName).id",
"#(1,action)": "#(2,id).#(1,typeName).action",
"*": "#(2,id).#(1,typeName).&1.&"
},
"*": "#(1,id).#(1,type.typeName).&"
}
},
"type": {
"#(1,id)": "#(2,id).#(1,typeName).id",
"#(1,action)": "#(2,id).#(1,typeName).action",
"*": "#(2,id).#(1,typeName).&1.&"
},
"*": "#(1,id).#(1,type.typeName).&"
}
}
}
},
{
// Filter out by the desired value(in this case it's "xx")
"operation": "shift",
"spec": {
"*": {
"xx": ""
}
}
},
{
// Get rid of same repeating components of id and action arrays
"operation": "cardinality",
"spec": {
"*": {
"id": "ONE",
"action": "ONE"
}
}
},
{
// Get rid of undesired attributes
"operation": "remove",
"spec": {
"*": {
"type": {
"typeGroup": "",
"typeValue": ""
},
"det*": {
"reason": ""
}
}
}
}
]
You may try library Josson. Function map() build a new ObjectNode. Function field() modify the current ObjectNode and can be used to remove fields. The transformation statement is short and easy to understand.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString(inputJSON);
JsonNode node = josson.getNode(
"cumulateCollect(" +
" [type.typeName='xx']" + // filter
" .field(type.map(id, typeName)" + // need "type.id" and "type.typeName"
" ,details.field(reason:)" + // remove "details.reason"
" ,item:)" + // remove "item"
" ,item)"); // next round
System.out.println(node.toPrettyString());
I have a complex issue here and some advice or suggestions would be greatly appreciated. Essentially I have a complex JSON schema that looks something like this:
{
"$schema": "http://example.org",
"$id": "http://example.org",
"title": "schema title",
"description": "description",
"properties": {
"name": {
"description": "description",
"type": "string",
"enum": [
"name1",
"name2"
]
},
"storage": {
"description": "description",
"type": "integer",
"minimum": "200",
"maximum": "500",
"default": "200",
},
"domain": {
"description": "description",
"type": "string"
},
},
"if": {
"properties": {
"name": {
"const": "name1"
}
}
},
"then": {
"if": {
"properties": {
"version": {
"const": "version1"
}
}
},
"then": {
"properties": {
"cpus": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 8
},
"memory": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 32
},
},
"required": [
"cpus",
"memory"
]
},
"else": {
"if": {
"properties": {
"version": {
"const": "version2"
}
}
},
"then": {
"properties": {
"cpus": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 8
},
"diskSize": {
"description": "description",
"type": "integer",
"minimum": 250,
"maximum": 1000
},
},
"required": [
"cpus",
"diskSize"
]
}
}
},
"else": {
"if": {
"properties": {
"name": {
"const": "name2"
}
}
},
"then": {
"if": {
"properties": {
"version": {
"const": "version3"
}
}
},
"then": {
"properties": {
"diskSize": {
"description": "description",
"type": "integer",
"minimum": 100,
"maximum": 500
}
"memory": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 28
}
},
"required": [
"diskSize",
"memory"
]
},
"else": {
"if": {
"properties": {
"version": {
"const": "version4"
}
}
},
"then": {
"properties": {
"cpus": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 28
},
"memory": {
"description": "description",
"type": "integer",
"minimum": 1,
"maximum": 64
}
},
"required": [
"cpus",
"memory"
]
}
}
}
}
}
I need to build a JSON object using this schema in java. Every property in the schema is inside of a map that I have access to, so I can quite simply just get the property from the map and add it to a JsonNode object that I am building. Every property under the initial "properties" object is easy to retrieve, I can just get a list of them and then get each one from the map.
The complexity lies in the if/then/else part of the json schema. The only way I can see to find which property I need is to first build the initial part of the json from the first "properties" object and then have some sort of quite complex recursive algorithm that goes into every if/then/else statement and compares the value of the property being evaluated and then returns a list of the properties I need to get from the map. I have looked around online for a library that can build Json from a Json schema in java but haven't found anything that can deal with the complex if/then/else statements.
Any suggestions or ideas would be greatly appreciated.
I have three JSON arrays named item, topping and batter.
Now I want to store this data into single json object. It is highly nested data I don't know how to store data into single object like this.
Resultant object should be like this:
{
"item": [{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batter": [{
"id": "1001",
"type": "Regular"
},
{
"id": "1002",
"type": "Chocolate"
},
{
"id": "1003",
"type": "Blueberry"
},
{
"id": "1004",
"type": "Devil's Food"
}
],
"topping": [{
"id": "5001",
"type": "None"
},
{
"id": "5002",
"type": "Glazed"
},
{
"id": "5005",
"type": "Sugar"
},
{
"id": "5007",
"type": "Powdered Sugar"
},
{
"id": "5006",
"type": "Chocolate with Sprinkles"
},
{
"id": "5003",
"type": "Chocolate"
},
{
"id": "5004",
"type": "Maple"
}
]
},
{
"id": "0002",
"type": "donut",
"name": "Raised",
"ppu": 0.55,
"batter": [{
"id": "1001",
"type": "Regular"
}],
"topping": [{
"id": "5001",
"type": "None"
},
{
"id": "5002",
"type": "Glazed"
},
{
"id": "5005",
"type": "Sugar"
},
{
"id": "5003",
"type": "Chocolate"
},
{
"id": "5004",
"type": "Maple"
}
]
},
{
"id": "0003",
"type": "donut",
"name": "Old Fashioned",
"ppu": 0.55,
"batter": [{
"id": "1001",
"type": "Regular"
},
{
"id": "1002",
"type": "Chocolate"
}
],
"topping": [{
"id": "5001",
"type": "None"
},
{
"id": "5002",
"type": "Glazed"
},
{
"id": "5003",
"type": "Chocolate"
},
{
"id": "5004",
"type": "Maple"
}
]
}
]
}
I am new to the Elastic search world.Basically I am trying to retrieve the nested objects based on the ID.This is the JSON representation of my document.
{
"_index": "xyz",
"_type": "abc",
"_id": "12",
"_version": 1,
"found": true,
"_source":
{
"lastModifiedBy": "12",
"lastModifiedDate": "2015-12-31T19:45:29.493Z",
"profile":
[
{
"type": "nested",
"views":
[
{
"type": "nested",
"id": "view1",
"name": "view1",
"properties":
[
{
"name": "default",
"value": false
}
],
"widgets":
[
{
"type": "nested",
"id": "graph",
"name": "graph",
"elementId": "ui_graph",
"properties":
[
{
"name": "currency",
"value": "YEN"
}
]
}
]
}
} ] } ]
I am trying to get the widgets based on the view id.This is the my search query.
"query" : {
"term" : {
"_id" : "12"
}
},
"post_filter" : {
"nested" : {
"query" : {
"filtered" : {
"query" : {
"match_all" : { }
},
"filter" : {
"term" : {
"profile.views.id" : "view1"
}
}
}
},
"path" : "profile.views"
}
}
}
I am not sure what is wrong here.But getting "nested object under path [profile.views] is not of nested type]".
Below is my mapping structure
{
"xyz": {
"mappings": {
"abc": {
"properties": {
"lastModifiedBy": {
"type": "string"
},
"lastModifiedDate": {
"type": "date",
"format": "dateOptionalTime"
},
"name": {
"type": "string"
},
"profile": {
"properties": {
"lastModifiedBy": {
"type": "string"
},
"lastModifiedDate": {
"type": "date",
"format": "dateOptionalTime"
},
"type": {
"type": "string"
},
"views": {
"properties": {
"id": {
"type": "string"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"properties": {
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {
"type": "string"
}
}
},
"type": {
"type": "string"
},
"viewId": {
"type": "string"
},
"widgets": {
"properties": {
"elementId": {
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"properties": {
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {
"type": "string"
}
}
},
"type": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
Please help!
You are getting the error because you have not specified type as nested for profile and views. Refer to the Docs for how to created nested objects . You should be defining type as nested for every nested object like this
{
"xyz": {
"mappings": {
"abc": {
"properties": {
"lastModifiedBy": {
"type": "string"
},
"lastModifiedDate": {
"type": "date",
"format": "dateOptionalTime"
},
"name": {
"type": "string"
},
"profile": {
"type": "nested", <--- here, you need this for every nested object
"properties": {
"lastModifiedBy": {
"type": "string"
},
"lastModifiedDate": {
"type": "date",
"format": "dateOptionalTime"
},
"type": {
"type": "string"
},
"views": {
"type": "nested",
"properties": {
"id": {
"type": "string"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"properties": {
"type": "nested",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {
"type": "string"
}
}
},
"type": {
"type": "string"
},
"viewId": {
"type": "string"
},
"widgets": {
"type": "nested",
"properties": {
"elementId": {
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"properties": {
"type": "nested",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {
"type": "string"
}
}
},
"type": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
Hope this helps!!
I have problems with using jsonschema2pojo in my code.
So, I use jsonschema2pojo generator(http://www.jsonschema2pojo.org/) to generate POJO's from this URL: http://store.steampowered.com/api/appdetails/?appids=10
Finally, I create all these class in one package 'model'.
Then in I try use it to read json from link but I receive exception:
Exception in thread "main" java.lang.NullPointerException
at controller.View.main(View.java:26)
I don't understand why.
Here is code How I can do it:
public class View {
private static String urlStr = "http://store.steampowered.com/api/appdetails/?appids=10";
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
try {
Game game = objectMapper.readValue(new URL(urlStr), Game.class);
System.out.println(game.getData().getName());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The Game.class is finall class which I set in generator as 'Class name'.
What is wrong? How can I get all these data from this link?
In maven I configure it like that:
<plugin>
<groupId>org.jsonschema2pojo</groupId>
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
<version>0.4.18</version>
<configuration>
<sourceType>jsonschema</sourceType>
<outputEncoding>${project.build.sourceEncoding}</outputEncoding>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
<annotationStyle>jackson2</annotationStyle>
<generateBuilders>true</generateBuilders>
<initializeCollections>true</initializeCollections>
</configuration>
<executions>
<execution>
<id>generate-game</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<targetPackage>model.game</targetPackage>
<sourceDirectory>${basedir}/src/main/resources/schema/game</sourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
As I said, all classes are in src/main/java and package 'model', my schema is in sr/main/resources/schema/ in file 'game'
And my schema looks like:
{
"type": "object",
"required":false,
"javaType":"model.Game",
"properties": {
"10": {
"id": "10",
"type": "object",
"properties": {
"success": {
"id": "success",
"type": "boolean"
},
"data": {
"id": "data",
"type": "object",
"properties": {
"type": {
"id": "type",
"type": "string"
},
"name": {
"id": "name",
"type": "string"
},
"steam_appid": {
"id": "steam_appid",
"type": "integer"
},
"required_age": {
"id": "required_age",
"type": "integer"
},
"is_free": {
"id": "is_free",
"type": "boolean"
},
"detailed_description": {
"id": "detailed_description",
"type": "string"
},
"about_the_game": {
"id": "about_the_game",
"type": "string"
},
"supported_languages": {
"id": "supported_languages",
"type": "string"
},
"header_image": {
"id": "header_image",
"type": "string"
},
"website": {
"id": "website",
"type": "null"
},
"pc_requirements": {
"id": "pc_requirements",
"type": "object",
"properties": {
"minimum": {
"id": "minimum",
"type": "string"
}
}
},
"mac_requirements": {
"id": "mac_requirements",
"type": "object",
"properties": {
"minimum": {
"id": "minimum",
"type": "string"
}
}
},
"linux_requirements": {
"id": "linux_requirements",
"type": "object",
"properties": {
"minimum": {
"id": "minimum",
"type": "string"
}
}
},
"developers": {
"id": "developers",
"type": "array",
"items": {
"id": "0",
"type": "string"
}
},
"publishers": {
"id": "publishers",
"type": "array",
"items": {
"id": "0",
"type": "string"
}
},
"price_overview": {
"id": "price_overview",
"type": "object",
"properties": {
"currency": {
"id": "currency",
"type": "string"
},
"initial": {
"id": "initial",
"type": "integer"
},
"final": {
"id": "final",
"type": "integer"
},
"discount_percent": {
"id": "discount_percent",
"type": "integer"
}
}
},
"packages": {
"id": "packages",
"type": "array",
"items": [
{
"id": "0",
"type": "string"
},
{
"id": "1",
"type": "integer"
},
{
"id": "2",
"type": "integer"
},
{
"id": "3",
"type": "integer"
}
]
},
"package_groups": {
"id": "package_groups",
"type": "array",
"items": {
"id": "0",
"type": "object",
"properties": {
"name": {
"id": "name",
"type": "string"
},
"title": {
"id": "title",
"type": "string"
},
"description": {
"id": "description",
"type": "string"
},
"selection_text": {
"id": "selection_text",
"type": "string"
},
"save_text": {
"id": "save_text",
"type": "string"
},
"display_type": {
"id": "display_type",
"type": "integer"
},
"is_recurring_subscription": {
"id": "is_recurring_subscription",
"type": "string"
},
"subs": {
"id": "subs",
"type": "array",
"items": [
{
"id": "0",
"type": "object",
"properties": {
"packageid": {
"id": "packageid",
"type": "string"
},
"percent_savings_text": {
"id": "percent_savings_text",
"type": "string"
},
"percent_savings": {
"id": "percent_savings",
"type": "integer"
},
"option_text": {
"id": "option_text",
"type": "string"
},
"option_description": {
"id": "option_description",
"type": "string"
},
"can_get_free_license": {
"id": "can_get_free_license",
"type": "string"
},
"is_free_license": {
"id": "is_free_license",
"type": "boolean"
},
"price_in_cents_with_discount": {
"id": "price_in_cents_with_discount",
"type": "integer"
}
}
},
{
"id": "1",
"type": "object",
"properties": {
"packageid": {
"id": "packageid",
"type": "integer"
},
"percent_savings_text": {
"id": "percent_savings_text",
"type": "string"
},
"percent_savings": {
"id": "percent_savings",
"type": "integer"
},
"option_text": {
"id": "option_text",
"type": "string"
},
"option_description": {
"id": "option_description",
"type": "string"
},
"can_get_free_license": {
"id": "can_get_free_license",
"type": "string"
},
"is_free_license": {
"id": "is_free_license",
"type": "boolean"
},
"price_in_cents_with_discount": {
"id": "price_in_cents_with_discount",
"type": "integer"
}
}
},
{
"id": "2",
"type": "object",
"properties": {
"packageid": {
"id": "packageid",
"type": "integer"
},
"percent_savings_text": {
"id": "percent_savings_text",
"type": "string"
},
"percent_savings": {
"id": "percent_savings",
"type": "integer"
},
"option_text": {
"id": "option_text",
"type": "string"
},
"option_description": {
"id": "option_description",
"type": "string"
},
"can_get_free_license": {
"id": "can_get_free_license",
"type": "string"
},
"is_free_license": {
"id": "is_free_license",
"type": "boolean"
},
"price_in_cents_with_discount": {
"id": "price_in_cents_with_discount",
"type": "integer"
}
}
},
{
"id": "3",
"type": "object",
"properties": {
"packageid": {
"id": "packageid",
"type": "integer"
},
"percent_savings_text": {
"id": "percent_savings_text",
"type": "string"
},
"percent_savings": {
"id": "percent_savings",
"type": "integer"
},
"option_text": {
"id": "option_text",
"type": "string"
},
"option_description": {
"id": "option_description",
"type": "string"
},
"can_get_free_license": {
"id": "can_get_free_license",
"type": "string"
},
"is_free_license": {
"id": "is_free_license",
"type": "boolean"
},
"price_in_cents_with_discount": {
"id": "price_in_cents_with_discount",
"type": "integer"
}
}
}
]
}
}
}
},
"platforms": {
"id": "platforms",
"type": "object",
"properties": {
"windows": {
"id": "windows",
"type": "boolean"
},
"mac": {
"id": "mac",
"type": "boolean"
},
"linux": {
"id": "linux",
"type": "boolean"
}
}
},
"metacritic": {
"id": "metacritic",
"type": "object",
"properties": {
"score": {
"id": "score",
"type": "integer"
},
"url": {
"id": "url",
"type": "string"
}
}
},
"categories": {
"id": "categories",
"type": "array",
"items": [
{
"id": "0",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"description": {
"id": "description",
"type": "string"
}
}
},
{
"id": "1",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"description": {
"id": "description",
"type": "string"
}
}
}
]
},
"genres": {
"id": "genres",
"type": "array",
"items": {
"id": "0",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "string"
},
"description": {
"id": "description",
"type": "string"
}
}
}
},
"screenshots": {
"id": "screenshots",
"type": "array",
"items": [
{
"id": "0",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "1",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "2",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "3",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "4",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "5",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "6",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "7",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "8",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "9",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "10",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "11",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
},
{
"id": "12",
"type": "object",
"properties": {
"id": {
"id": "id",
"type": "integer"
},
"path_thumbnail": {
"id": "path_thumbnail",
"type": "string"
},
"path_full": {
"id": "path_full",
"type": "string"
}
}
}
]
},
"recommendations": {
"id": "recommendations",
"type": "object",
"properties": {
"total": {
"id": "total",
"type": "integer"
}
}
},
"achievements": {
"id": "achievements",
"type": "object",
"properties": {
"total": {
"id": "total",
"type": "integer"
}
}
},
"release_date": {
"id": "release_date",
"type": "object",
"properties": {
"coming_soon": {
"id": "coming_soon",
"type": "boolean"
},
"date": {
"id": "date",
"type": "string"
}
}
},
"support_info": {
"id": "support_info",
"type": "object",
"properties": {
"url": {
"id": "url",
"type": "string"
},
"email": {
"id": "email",
"type": "string"
}
}
},
"background": {
"id": "background",
"type": "string"
}
}
}
},
"required": [
"success",
"data",
"id",
"path_thumbnail",
"path_full"
]
}
},
"required": [
"10"
]
}
With this config I can read all data using this line instead of before System.out.println:
System.out.println( objectMapper.writeValueAsString( game ) );
Now I can see full json string, but how can I read it using 'game.getData().getName()' etc ?
The JSON schema that you gave defines "10" as a top-level property containing everything else.
{
"type": "object",
"required":false,
"javaType":"model.Game",
"properties": {
"10": {
"id": "10",
"type": "object",
"properties": {
"success": {
"id": "success",
"type": "boolean"
},
"data": {
"id": "data",
"type": "object",
"properties": {
"type": {
"id": "type",
"type": "string"
},
"name": {
"id": "name",
"type": "string"
},
...
jsonschema2pojo reads that schema and generates a class named _10 as part of the model. You can find this in _10.java in the generated-sources directory.
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"success",
"data"
})
public class _10 {
I don't know if this was intentional in the schema. If it's intentional, then you would need to access the name like this.
System.out.println(game.get10().getData().getName());
I was able to get it working after making that change.
EDIT: It appears these JSON messages are expected to have a variable ID as a top-level key containing everything else, so we need to handle this a bit differently. We can generalize the schema so that it isn't specific to a particular ID, and then we can use a two-step process to parse the message. First, we'll use [ObjectMapper#readValue](https://fasterxml.github.io/jackson-databind/javadoc/2.5/com/fasterxml/jackson/databind/ObjectMapper.html#readValue(java.net.URL, com.fasterxml.jackson.core.type.TypeReference)) to parse the whole message into a Map<String, Object>. We expect that top-level map will contain a single element, corresponding to the ID of the document we requested, and its value will be a sub-map containing the values we're interested in. We'll then pass that sub-map through [ObjectMapper#convertValue](https://fasterxml.github.io/jackson-databind/javadoc/2.5/com/fasterxml/jackson/databind/ObjectMapper.html#convertValue(java.lang.Object, java.lang.Class)) to get the Game object that we want.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cnauroth</groupId>
<artifactId>test-json</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Test JSON</name>
<description>Test JSON</description>
<build>
<plugins>
<plugin>
<groupId>org.jsonschema2pojo</groupId>
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
<version>0.4.18</version>
<configuration>
<sourceType>jsonschema</sourceType>
<outputEncoding>${project.build.sourceEncoding}</outputEncoding>
<outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
<annotationStyle>jackson2</annotationStyle>
<generateBuilders>true</generateBuilders>
<initializeCollections>true</initializeCollections>
</configuration>
<executions>
<execution>
<id>generate-game</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<targetPackage>model.game</targetPackage>
<sourceDirectory>${basedir}/src/main/resources/schema/game</sourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<mainClass>View</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>${project.artifactId}</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
game
The JSON schema file is too large to paste, so here is a link to a gist.
View.java
import java.net.URI;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Game;
public class View {
public static void main(String[] args) throws Exception {
String id = args[0];
URI uri = new URI("http", "store.steampowered.com", "/api/appdetails",
"appids=" + id, null);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> obj = objectMapper.readValue(uri.toURL(),
new TypeReference<Map<String, Object>>(){});
Game game = objectMapper.convertValue(obj.get(id), Game.class);
System.out.println(game.getData().getName());
}
}
Demo
> mvn clean package
> java -jar target/test-json.jar 10
Counter-Strike
> java -jar target/test-json.jar 219
Half-Life 2: Demo
Additionally, I needed to make one other change to the schema. I removed the linux_requirements property. This is because looking at document 10 vs. document 219, it's an object in the former and it's an array in the latter. For document 219, the parser didn't know how to make sense of the array, because we were expecting an object. It's odd for data returned from an API to be inconsistent like this. You might need to do some more tinkering with the schema to get this fully working for all possible JSON documents returned from that API.
I have put all of the code into a GitHub repository. You can take that code, build it and run it to see it working. Then, you can adapt it further to your needs.
Instead of trying to model the object containing the ids with properties, use additionalProperties. Assuming that the top level type contains several games, you could try replacing
{
"type": "object",
"required":false,
"javaType":"model.Game",
"properties": {
"10": {
"id": "10",
"type": "object",
...
}
}
}
with something like
{
"type": "object",
"required":false,
"javaType":"model.Games",
"additionalProperties": {
"id": "game",
"type": "object",
"javaType": "model.Game"
...
}
}
This should make a model.Games type with a method like public Map<String, Game> getAdditionalProperties().