Issue with a Json schema validation - java

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.

Related

Json deserialization with key value mapped object

I'm trying to deserialize a json string to Java object. Here is my json string.
{
"header": {
"transactionId": "12345",
"application": "testApp"
},
"items": {
"item": [
{
"attributes": {
"attribute": [
{
"key": "accountType",
"value": "TYPE1"
},
{
"key": "accountId",
"value": "123"
}
]
}
},
{
"attributes": {
"attribute": [
{
"key": "userType",
"value": "TYPE2"
},
{
"key": "userId",
"value": "321"
}
]
}
}
]
}
}
And I want to deserialize this json to Java classes that shown as below.
public class Response {
private Header header;
private List<Object> items;
//getters and setters
}
public class Header {
private String transactionId;
private String application;
//getters and setters
}
public class Account {
private String accountType;
private String accountId;
//getters and setters
}
public class User {
private String userType;
private String userId;
//getters and setters
}
How can I deserialize by using jackson or objectmapper etc.? The main problem is the field names are in the attribute object and the value of 'key' field. Is it possible to find the right field on Java object by using value of the 'key' field and to set the right value with the value of the 'value' field?
One possible solution is to transform the JSON structure before converting to POJO.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString(yourJsonString);
JsonNode node = josson.getNode(
"map(header," +
" items: items.item#" +
" .attributes.attribute" +
" .map(key::value)" +
" .mergeObjects()" +
" )");
System.out.println(node.toPrettyString());
Output
{
"header" : {
"transactionId" : "12345",
"application" : "testApp"
},
"items" : [ {
"accountType" : "TYPE1",
"accountId" : "123"
}, {
"userType" : "TYPE2",
"userId" : "321"
} ]
}

#CompletionField annotation doesn't create field on mapping

I am trying to make autocomplete suggestion using Elasticsearch. Here is my entity:
#Document(indexName = ESUtil.INDEX_NAME)
public class Person {
#Id
private String id;
#Field(type = FieldType.Text, name = "name")
private String name;
#Field(type = FieldType.Text, name = "surname")
private String surname;
#Field(type = FieldType.Integer, name = "age")
private int age;
#Field(type = FieldType.Text, name = "job")
private String job;
#CompletionField
private Completion suggest;
// getters-setters...
}
I am creating documents with this method:
#Autowired
private ElasticsearchRestTemplate elastic;
public String createOrUpdateDocument(Person person) {
IndexQuery query = new IndexQueryBuilder().withId(person.getId()).withObject(person).build();
return elastic.index(query, IndexCoordinates.of(ESUtil.INDEX_NAME));
}
And it is creating the index automatically with these config after create a document:
{
"dummy": {
"aliases": {},
"mappings": {
"properties": {
"_class": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"age": {
"type": "long"
},
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"job": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"surname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "dummy",
"creation_date": "1674206424438",
"number_of_replicas": "1",
"uuid": "a_XWdCzxRUieyDZrioIaSg",
"version": {
"created": "8060099"
}
}
}
}
}
So my question is, where is my "suggest" field with "completion" type? I am using spring-data-elasticsearch (ver: 4.4.2), elasticsearch (ver: 8.6.0) and there is not any additional configuration.
Am I missing some config or should I create index by myself?
The mapping of an index is only created by Spring Data Elasticsearch when you are using Spring Data repositories:
during bootstrapping the repository support on application startup
(https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.repositories.autocreation).
So if you don't use repository access for your entities, or the index already exists, the mapping will not be written automatically.
You always can do that by your self:
elastic.indexOps(Person.class).putMapping(Person.class)

How to create class for dynamic JSON structure in Spring Boot Java

I have following JSON structure as input which can be nested and without nested. I want to fetch the JSON as input and process in Spring Boot application. How to create a class which dynamic key values in the JSON. It can be any key-value pairs in the JSON input. Below is the sample one.
Without Nested:
{
"mappings": {
"properties": {
"firstname": {
"type": "string"
},
"lastname": {
"type": "string"
},
"salary": {
"type": "integer"
},
"date_of_birth": {
"type": "date"
}
}
}
}
With Nested:
{
"mappings": {
"properties": {
"firstname": {
"type": "string"
},
"lastname": {
"type": "string"
},
"annual_salary": {
"type": "integer"
},
"date_of_birth": {
"type": "date"
},
"comments": {
"type": "nested",
"properties": {
"name": {
"type": "string"
},
"comment": {
"type": "string"
},
"age": {
"type": "short"
},
"stars": {
"type": "short"
},
"date": {
"type": "date"
}
}
}
}
}
}
I don't know how to create a class to support both with and without nested in single class. I have tried the following. It doesn't help.
public class Schema {
Mapping mappings;
public Mapping getMappings() {
return mappings;
}
public void setMappings(Mapping mappings) {
this.mappings = mappings;
}
public static class Mapping {
Property properties;
public Property getProperties() {
return properties;
}
public void setProperties(Property properties) {
this.properties = properties;
}
}
public static class Property {
Map<String, Map<String, Object>> field = new HashMap<>();
public Map<String, Map<String, Object>> getField() {
return field;
}
public void setField(Map<String, Map<String, Object>> field) {
this.field = field;
}
}
}
I have come across a scenario similar to this where it was possible that my JSON may not be having consistent Key-Value pairs.
I gave the following jackson annotation at class level so that whichever property not available in my model and which are present in the JSON will be ignored.
#JsonIgnoreProperties(ignoreUnknown = true)

Convert a Dynamic JSON String to a HashMap

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;
}

katharsis collection of non primitives serialization

Trying to serialize a collection of non-primitive types using katharsis, but getting an empty collection all the time.
Response example:
{
"data": {
"type": "products",
"id": "1",
"attributes": {
"simpleAttributes": [
{}
],
"variationGroup": "variationGroup"
},
"relationships": {},
"links": {
"self": "http://localhost:8080/api/products/1"
}
},
"included": []
}
Expected response:
{
"data": {
"type": "products",
"id": "1",
"attributes": {
"simpleAttributes": [
{
tittle: "some title",
value: "some value"
}
],
"variationGroup": "variationGroup"
},
"relationships": {},
"links": {
"self": "http://localhost:8080/api/products/1"
}
},
"included": []
}
Domain objects (getters, setters, constructor and other stuff omitted by using lombok #Data annotation):
#JsonApiResource(type = "products")
#Data
public class Product {
#JsonApiId
private Integer id;
private List<SimpleAttribute> simpleAttributes = new ArrayList<>();
private String variationGroup;
}
#Data
public class SimpleAttribute implements Serializable{
private String title;
private String value;
}
I do not want to use relationships in this case or to include attributes to "included" field. Is it possible in katharsis?
Not sure what actually was wrong, but the problem disappeared after I changed katharsis-spring version from 2.3.0 to 2.3.1.

Categories

Resources