I have devised a JSON structure to represent a table with header columns plus table rows that looks like the following.
{
"header": [
{
"fieldType": "STRING",
"readOnly": true,
"headerValue": "name"
},
{
"fieldType": "STRING",
"readOnly": true,
"headerValue": "description"
}
],
"rows": [
[
{
"fieldValue" : "engine"
},
{
"fieldValue" : "this is an engine"
}
],
[
{
"fieldValue" : "engine"
},
{
"fieldValue" : "this is an engine"
}
],
[
{
"fieldValue" : "engine"
},
{
"fieldValue" : "this is an engine"
}
],
[
{
"fieldValue" : "engine"
},
{
"fieldValue" : "this is an engine"
}
]
]
}
A row is for example
[
{
"fieldValue" : "engine"
},
{
"fieldValue" : "this is an engine"
}
]
The number of entries in a row matches the number of header columns. So "engine" is the "name" column and "this is an engine" is the "description" column
When I use GSON to turn my POJO's into a JSON String the closest I have got to match this structure is:
{
"header": [
{
"fieldType": "STRING",
"readOnly": true,
"headerValue": "name"
},
{
"fieldType": "STRING",
"readOnly": true,
"headerValue": "description"
}
],
"rows": [
{
"fieldValues": [
"engine",
"this is an engine"
]
},
{
"fieldValues": [
"engine",
"this is an engine"
]
},
{
"fieldValues": [
"engine",
"this is an engine"
]
},
{
"fieldValues": [
"engine",
"this is an engine"
]
}
]
}
Here's the code I'm using to test
enum FieldType {
STRING,
BOOLEAN,
NUMBER,
PHOTO,
PHOTOLIST;
}
class SurveyFields {
private List<SurveyColumn> header;
private List<SurveyRow> rows;
public List<SurveyColumn> getHeader() {
return header;
}
public List<SurveyRow> getRows() {
return rows;
}
public void setHeader(List<SurveyColumn> header) {
this.header = header;
}
public void setRows(List<SurveyRow> rows) {
this.rows = rows;
}
}
class SurveyColumn {
private FieldType fieldType;
private boolean readOnly;
private String headerValue;
public static class Builder {
private FieldType fieldType;
private boolean readOnly;
private String headerValue;
public Builder withFieldType(FieldType fieldType) {
this.fieldType = fieldType;
return this;
}
public Builder withReadOnly(boolean readOnly) {
this.readOnly = readOnly;
return this;
}
public Builder withHeaderValue(String headerValue) {
this.headerValue = headerValue;
return this;
}
public SurveyColumn build() {
return new SurveyColumn(fieldType, readOnly, headerValue);
}
}
public SurveyColumn(FieldType fieldType, boolean readOnly, String headerValue) {
this.fieldType = fieldType;
this.readOnly = readOnly;
this.headerValue = headerValue;
}
}
class SurveyRow {
public static class Builder {
private String[] fieldValues;
public Builder withFieldValues(String[] fieldValues) {
this.fieldValues = fieldValues;
return this;
}
public SurveyRow build() {
return new SurveyRow(fieldValues);
}
}
private String[] fieldValues;
public SurveyRow(String[] fieldValues) {
this.fieldValues = fieldValues;
}
}
public class TestGson {
public static void main(String[] args) {
SurveyFields fields = new SurveyFields();
fields.setHeader(Arrays.asList(new SurveyColumn[] {
new SurveyColumn.Builder().withHeaderValue("name").withFieldType(FieldType.STRING).withReadOnly(true)
.build(),
new SurveyColumn.Builder().withHeaderValue("description").withFieldType(FieldType.STRING)
.withReadOnly(true).build() }));
fields.setRows(Arrays.asList(new SurveyRow[] {
new SurveyRow.Builder().withFieldValues(new String[] { "engine", "this is an engine" }).build(),
new SurveyRow.Builder().withFieldValues(new String[] { "engine", "this is an engine" }).build(),
new SurveyRow.Builder().withFieldValues(new String[] { "engine", "this is an engine" }).build(),
new SurveyRow.Builder().withFieldValues(new String[] { "engine", "this is an engine" }).build()
}));
Gson gson = new Gson();
System.out.println(gson.toJson(fields));
}
}
How can I structure my POJO's to match the expected JSON output?
If you get the JSON from a third party, this site might help you generate POJO from JSON.
Related
I'm running into an issue with a Json schema validation.
I have a Java class like this
package com.ebc.jackson.exampe.data;
public class FormElement {
private String fieldName;
private String fieldType;
private Object value;
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldType() {
return fieldType;
}
public void setFieldType(String fieldType) {
this.fieldType = fieldType;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
I'm using below code to generate the schema:
JsonSchemaGenerator generator =
new JsonSchemaGenerator(objectMapper);
JsonNode schema = generator.generateJsonSchema(FormElement.class);
String strSchema = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
this code generates below schema
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Form Element",
"type" : "object",
"additionalProperties" : false,
"properties" : {
"fieldName" : {
"type" : "string"
},
"fieldType" : {
"type" : "string"
},
"value" : {
"$ref" : "#/definitions/Object"
}
},
"definitions" : {
"Object" : {
"type" : "object",
"additionalProperties" : false,
"properties" : { }
}
}
}
In the request, I could receive a Json Object like
{
"fieldName": "user-name",
"fieldType": "textInput",
"value": "Alex"
}
When I try to validate this object against the Json schema, "value" attribute is not valid, it is expecting a "key: value" pair but, a string is found.
In the Java class the value attribute is Object because it could be a String, Integer, Boolean, etc.
My question is, how can I generate a schema to support different data types for "value" attribute?
You can use the anyOf property of schema :
Try the below one :
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Form Element",
"type": "object",
"additionalProperties": false,
"properties": {
"fieldName": {
"type": "string"
},
"fieldType": {
"type": "string"
},
"value": {
"$ref": "#/definitions/Object"
}
},
"definitions": {
"Object": {
"format": "",
"anyOf": [
{
"type": "object"
},
{
"type": "string"
},
{
"type": "number"
},
{
"type": "integer"
},
{
"type": "boolean",
"x-enumFlags": true
},
{
"type": "null"
}
],
"additionalProperties": false,
"properties": {}
}
}
}
Or you could try the pipe separator in type.In the schema you provided just change the value of type to "type": "string|object|integer|number". This notation didn't work for me in swagger but you can check if it works for you.
I am trying to download a JSON file with the latest news from a link, and then fill a news page with news articles from the JSON file, but I can't get it to work.
This is my JSON file:
[
"sections": {
{
"title": "category 1",
"color": 2,
"posts": [
{
"title": "Test 1",
"date": 17-09-2019,
"images": {
"launcher_preview": "testimage.png",
"imageName2": "testimage.png"
},
"href": "https://testlink.com"
},
{
"title": "Test 2",
"date": 17-09-2019,
"images": {
"launcher_preview": "testimage2.png",
"imageName2": "testiamge2.png"
},
"href": "https://testlink2.com"
}
]
},
{
"title": "category 2",
"color": 2,
"posts": [
{
"title": "Test 3",
"date": 17-09-2019,
"images": {
"launcher_preview": "testimage3.png",
"imageName2": "testimage3.png"
},
"href": "https://testlink3.com"
}
]
}
}
]
My java class (Only included the necessary part):
public class NewsFeedManager extends ImageCache {
private static final String METADATA_URL = "https://Linkhiddenforprivacy.com/news/latest.json",
IMAGE_PROVIDER_URL = "https://Linkhiddenforprivacy.com/news/images/";
private static final int CACHE_TIME = 1000 * 60 * 20;
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
#JsonProperty
#Getter
private NewsFeed feed = new NewsFeed();
private Path imageCacheDir;
public NewsFeedManager() {
}
public static NewsFeedManager load(Launcher launcher) {
NewsFeedManager manager = Persistence.load(new File(launcher.getCacheDir(), "news_feed.json"), NewsFeedManager.class);
manager.imageCacheDir = Paths.get(launcher.getCacheDir().getAbsolutePath(), "launcher/news/images");
return manager;
}
public ListenableFuture<NewsFeed> refresh(boolean force) {
if (!force && this.feed != null && this.feed.expires > System.currentTimeMillis()) {
return Futures.immediateFuture(this.feed);
}
ListenableFuture<NewsFeed> future = this.executor.submit(() -> {
log.info("Fetching latest news feed from " + METADATA_URL);
NewsFeed feed = HttpRequest.get(HttpRequest.url(METADATA_URL))
.execute()
.expectResponseCode(200)
.returnContent()
.asJson(NewsFeed.class);
feed.expires = System.currentTimeMillis() + CACHE_TIME;
return feed;
});
Futures.addCallback(future, new FutureCallback<NewsFeed>() {
#Override
public void onSuccess(#Nullable NewsFeed result) {
NewsFeedManager.this.feed = result;
NewsFeedManager.this.save();
}
#Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
return future;
}
public ListenableFuture<Image> getImage(String resource) {
String remote = IMAGE_PROVIDER_URL + resource;
log.info("Fetching latest image feed from " + remote);
return this.obtain(resource, remote, false);
}
private void save() {
Persistence.commitAndForget(this);
}
public void clear() {
this.feed = null;
this.clearImageCache();
}
#Override
protected long getMaxCacheTime() {
return CACHE_TIME;
}
#Override
protected Path getImageCacheFolder() {
return this.imageCacheDir;
}
public static class NewsFeed {
#JsonProperty
#Getter
private List<NewsSection> sections;
#JsonProperty
private long expires;
}
public static class NewsSection {
#JsonProperty
#Getter
private String title;
#JsonProperty
#Getter
private int color;
#JsonProperty
#JsonManagedReference
#Getter
private List<NewsPost> posts;
}
public static class NewsPost {
#JsonProperty
#Getter
private String title;
#JsonProperty
#Getter
private Date date;
#JsonProperty
#Getter
private Map<String, String> images;
#JsonProperty
#Getter
private String href;
#JsonBackReference
#Getter
private NewsSection section;
}
I get this error when the client tries to fetch the news:
[info] Fetching latest news feed from https://linkhiddenforprivacy.com/news/latest.json
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.hiddenforprivacy.launcher.ui.resources.NewsFeedManager$NewsFeed out of START_ARRAY token
at [Source: java.io.StringReader#4ac13260; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:691)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:685)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromArray(BeanDeserializerBase.java:1215)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:126)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2986)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2091)
at com.skcraft.launcher.util.HttpRequest$BufferedResponse.asJson(HttpRequest.java:479)
at com.hiddenforprivacy.launcher.ui.resources.NewsFeedManager.lambda$refresh$0(NewsFeedManager.java:61)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
I am not sure what is causing the error, I think my JSON is not correctly formatted, but I am not sure, can anyone here see what is causing this error?
Thank you for your time,
Piet
If your object is in an array, you can't assign a key to it. As a result, your HttpRequest.asJson() is failing. I have edited your JSON to return your sections as an array of objects instead of a single array object containing the sections.
Also, you can't have dates as numbers in a JSON file. I converted them into strings as well. For standardization purposes, make sure you store the date as an ISO 8601 string in your actual file.
Try this edited version of your JSON:
[
{
"title": "category 1",
"color": 2,
"posts": [{
"title": "Test 1",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage.png",
"imageName2": "testimage.png"
},
"href": "https://testlink.com"
},
{
"title": "Test 2",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage2.png",
"imageName2": "testiamge2.png"
},
"href": "https://testlink2.com"
}
]
},
{
"title": "category 2",
"color": 2,
"posts": [{
"title": "Test 3",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage3.png",
"imageName2": "testimage3.png"
},
"href": "https://testlink3.com"
}]
}
]
[
"sections": {
{
I see two problems at the very start of the file.
One, the first character is a square bracket, indicating that the contained values will be a simple list. But then it goes straight into "sections" : {, which is a key/value syntax, indicating that we ought to be in a dictionary/hashmap context. But we aren't; we're in a list context.
Second, there are two opening braces following "sections":. What is the second one meant to indicate?
I see three problems
1. The wrong bracket around json object.
2. Sections is an array, but is missing the array syntax.
3. Date String is not a valid object type, the string should be wrapped with quotes.
Properly formatted json for an object with sections which is an array of two sections.
{"sections": [
{
"title": "category 1",
"color": 2,
"posts": [
{
"title": "Test 1",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage.png",
"imageName2": "testimage.png"
},
"href": "https://testlink.com"
},
{
"title": "Test 2",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage2.png",
"imageName2": "testiamge2.png"
},
"href": "https://testlink2.com"
}
]
},
{
"title": "category 2",
"color": 2,
"posts": [
{
"title": "Test 3",
"date": "17-09-2019",
"images": {
"launcher_preview": "testimage3.png",
"imageName2": "testimage3.png"
},
"href": "https://testlink3.com"
}
]
}]
}
Everytime I map an array of JSON object into POJOs, I always get the last object.
The JSON is an array of objects, and I have a POJO associate with each object of JSON. But when I mapped into an array of POJO, I always get 1 object.
Here is the JSON :
[ {
"event" : {
"Id" : "123456789",
"Name" : "An event1 name"
},
"branch" : {
"Id" : "112233445566",
"Name" : "A branch1 name",
},
"user" : {
"Id" : "9988776655",
"FirstName" : "John",
"LastName" : "Doe",
},
"event" : {
"Id" : "abcdef",
"Name" : "An event2 name"
},
"branch" : {
"Id" : "885522",
"Name" : "A branch2 name",
},
"user" : {
"Id" : "996633",
"FirstName" : "Jane",
"LastName" : "Doe",
}
} ]
The POJOs:
public class RdV {
private Event event;
private Branch branch;
private User user;
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch= branch;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public String toString() {
return "RdV [\n"
+ event + "\n"
+ branch + "\n"
+ user + "\n"
+ "]";
}
}
public class Event {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
}
The other POJOS, Branch and User are similar to Event.
And when I use the following :
List<RdV> rdv = Arrays.asList(objectMapper.readValue(jsonString, RdV[].class));
all I got is just the last object ( the size of list is 1, instead of 2 ):
==> RdV [
Event {
id="abcdef",
name="An event2 name"
}
Branch {
id="885522",
name="A branch2 name"
}
User {
id="996633",
lastName="Doe",
firstName="Jane"
}
]
Any idea what went wrong the code ?
Thanks.
your JSON doesn't have two separate objects - just one with double properties of Branch, Event and User, you should change it to
[
{
"event": {
"Id": "123456789",
"Name": "An event1 name"
},
"branch": {
"Id": "112233445566",
"Name": "A branch1 name"
},
"user": {
"Id": "9988776655",
"FirstName": "John",
"LastName": "Doe"
}
},
{
"event": {
"Id": "abcdef",
"Name": "An event2 name"
},
"branch": {
"Id": "885522",
"Name": "A branch2 name"
},
"user": {
"Id": "996633",
"FirstName": "Jane",
"LastName": "Doe"
}
}
]
I am running the following aggregation lookup query in spring but it doesn't seems to output the same result as one i run in mongo shell. I am guessing mongo shell knows that from:"testModel" collection exists but how do spring know "testModel" exists as we are only pass strings in Aggregation.lookup("testModel"). I can run all other stages fine for example match or project..
// code running in Mongo Shell
db.demoModel.aggregate([{
$lookup:
{
from: "testModel",
localField: "_id",
foreignField: "makeId",
as: "cool"
}
}]
)
// code from spring
public List<DemoModel> getSomeAutos() throws Exception {
LookupOperation lookupStage = Aggregation.lookup(
"testModel",
"_id",
"makeId",
"cool"
);
Aggregation aggregation = Aggregation.newAggregation(lookupStage);
List<DemoModel> demomode = mongoTemplate.aggregate(aggregation, "demoModel", DemoModel.class).getMappedResults();
return demomode;
}
// DemoModel.class
public class DemoModel {
#Id
private String id;
private String value;
private String label;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
//Mongo demoModel Collection
{
"_id" : ObjectId("5a8ee0d815dc17aa32f90f4b"),
"value" : "Mitsubishi",
"label" : "Mitsubishi"
}
{
"_id" : ObjectId("5a8ee0d815dc17aa32f90f4c"),
"value" : "BMW",
"label" : "BMW"
}
// Mongo testModel Collection
{
"_id" : ObjectId("5a8ee393b80c346266f25aba"),
"make" : "Mitsubishi",
"label" : "3000GT",
"value" : "3000GT",
"makeId" : ObjectId("5a8ee0d815dc17aa32f90f4b")
}
{
"_id" : ObjectId("5a8ee393b80c346266f25af8"),
"make" : "BMW",
"label" : "Alpina A2",
"value" : "Alpina A2",
"makeId" : ObjectId("5a8ee0d815dc17aa32f90f4c")
}
// Response i am getting
[
{
"id": "5a8ee0d815dc17aa32f90f4b",
"value": "Mitsubishi",
"label": "Mitsubishi"
},
{
"id": "5a8ee0d815dc17aa32f90f4c",
"value": "BMW",
"label": "BMW"
}
]
// expected response in this format
{
"_id" : ObjectId("5a8ee0d815dc17aa32f90f4b"),
"value" : "Mitsubishi",
"label" : "Mitsubishi",
"cool" : [
{
"_id" : ObjectId("5a8ee393b80c346266f25aba"),
"make" : "Mitsubishi",
"label" : "3000GT",
"value" : "3000GT",
"makeId" : ObjectId("5a8ee0d815dc17aa32f90f4b")
}]
}
I have a JSONObject with some dynamic attributes that I want to convert into a class, I have tried a lot of examples on SO, but no solution.
My json string looks like this
{
"result": {
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"assets": {
"r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH": [
{
"currency": "BTC",
"value": "5444166510000000e-26"
}
],
"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8": [
{
"currency": "EUR",
"value": "4000000000000000e-27"
}
],
"rPU6VbckqCLW4kb51CWqZdxvYyQrQVsnSj": [
{
"currency": "BTC",
"value": "1029900000000000e-26"
}
],
"rpR95n1iFkTqpoy1e878f4Z1pVHVtWKMNQ": [
{
"currency": "BTC",
"value": "4000000000000000e-30"
}
],
"rwmUaXsWtXU4Z843xSYwgt1is97bgY8yj6": [
{
"currency": "BTC",
"value": "8700000000000000e-30"
}
]
},
"balances": {
"rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ": [
{
"currency": "EUR",
"value": "29826.1965999999"
}
],
"ra7JkEzrgeKHdzKgo4EUUVBnxggY4z37kt": [
{
"currency": "USD",
"value": "13857.70416"
}
]
},
"ledger_hash": "980FECF48CA4BFDEC896692C31A50D484BDFE865EC101B00259C413AA3DBD672",
"ledger_index": 14483212,
"obligations": {
"BTC": "5908.324927635318",
"EUR": "992471.7419793958",
"GBP": "4991.38706013193",
"USD": "1997134.20229482"
},
"status": "success",
"validated": true
}
}
Is there something that I can use from the json.org or ObjectMapper?
The only part that is given me problem is the assets and the balances, I will appreciate all help in right direction
You should be able to deserialize this into classes like:
public class Response {
private Result result;
}
public class Result {
private String account;
private Map<String, List<Asset>> assets;
private Map<String, List<Asset>> balances;
private String ledger_hash;
private String ledger_index;
private Map<String, String> obligations;
private String status;
private boolean validated;
}
public class Asset {
private String currency;
private String value;
}