Jackson JSON SubTypes: Cannot Deserialize - java

I'm getting the following error
"JSON parse error: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.foo.model.conditionfields.BaseConditionField; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT),
My BaseConditionField Class is
#Data
public abstract class BaseConditionField implements Serializable {
protected ConditionType type;
protected ConditionOperator op;
#JsonProperty("op")
public void setOp(ConditionOperator op) {
if (!getSupportedOperators().contains(op)) {
throw new ValidationException(
format("Operator '%s' is not supported for '%s'", op, this.getClass().getSimpleName())
);
}
this.op = op;
}
#JsonProperty("type")
public void setType(String value) {
this.type = ConditionType.forString(value);
}
public String getType() {
return this.type.toString();
}
protected abstract Set<ConditionOperator> getSupportedOperators();
}
And the Field is
#Data
public class EvaluateConditionField extends BaseConditionField {
#Setter
#Getter(AccessLevel.NONE)
#ConditionType.TypeInfo
private List<BaseConditionField> condition;
#Override
protected Set<ConditionOperator> getSupportedOperators() {
return of(ConditionOperator.AND, ConditionOperator.OR);
}
}
Which should get converted from JSON
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private EvaluateConditionField conditions;
Its supposed to have a recursive chain of And/Or Clause.
My Type definition is
#Getter
#NoArgsConstructor(access = AccessLevel.PRIVATE)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum ConditionType {
EVALUATE_NESTED("evaluate.nested", EvaluateConditionField.class),
PROJECT_FIELD("project.field", ProjectConditionField.class);
private String value;
private Class<? extends BaseConditionField> filterClazz;
#JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static ConditionType forString(String value) {
return stream(ConditionType.values())
.filter(v -> v.value.equals(value))
.findFirst()
.orElseThrow(
() -> new ValidationException(String.format("Invalid Condition Type : %s", value))
);
}
#Override
#JsonValue
public String toString() {
return this.value;
}
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "type"
)
#JsonSubTypes(
{
#JsonSubTypes.Type(value = ProjectConditionField.class, name = "project.field"),
#JsonSubTypes.Type(value = EvaluateConditionField.class, name = "evaluate.nested")
}
)
public #interface TypeInfo {
}
}
JSON
[
{
"name": "Test Action Opp",
"accessRoles": [
"lead",
"collaborator"
],
"conditions": {
"type": "evaluate.nested",
"op": "or",
"condition": [
{
"type": "evaluate.nested",
"op": "and",
"condition": [
{
"op": "eq",
"type": "project.field",
"value": "In process"
}
]
}
]
},
"actions": [
{
"type": "set.field.value",
"actionInfo": {
"fieldId": "forecast_status",
"sectionId": "sales_details",
"source": "defined",
"value": "Verify"
}
}
],
"displayConfig": "\"{}\"",
"isAutomated": false
}
]
Not sure what the issue is. I tried changing it to normal array and it didnt work either. I think I defined these correctly.

Related

How to deserialize JSON via JsonTypeInfo with unknown property

I need to deserialize JSON looks like the following:
{
"data": [{
"id": "id1",
"type": "type1"
"name": "John",
...
},
{
"id": "id2",
"type": "type2",
"name": "Rebecca",
...
},
{
"id": "id3",
"type": "unknown",
"name": "Peter",
...
}]
}
For deserializing JSON which I have written above I have created a couple of classes:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type",
defaultImpl = DefaultData.class
)
#JsonSubTypes({
#JsonSubTypes.Type(value = Type1Data.class, name = "type1"),
#JsonSubTypes.Type(value = Type2Data.class, name = "type2")
})
public class AbstractData {
public final String id;
public final String type;
public final String name;
public AbstractData(String id, String type, String name) {
this.id = id;
this.type = type;
this.name = name;
}
}
public class Type1Data extends AbstractData {
#JsonCreator
public Type1Data(#JsonProperty("id") String id,
#JsonProperty("name") String name
) {
super(id, "type1", name);
}
}
public class DefaultData extends AbstractData {
#JsonCreator
public DefaultData(#JsonProperty("id") String id,
#JsonProperty("type") String type,
#JsonProperty("name") String name
) {
super(id, type, name);
}
}
public class Main {
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
AbstractData data = mapper.readValue(json, AbstractData.class);
}
}
I get an exception if I use default implementation:
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'unknown' as a type
The class DefaultData I need to avoid a deserialization exception if I will get the unknown type.
How can I fix this issue?
Summary
Right now it is not clear what is the exact root cause of the problem, because your example works for me with several corrections.
Still, please, consider the corrections as a draft.
Corrections
Data class for root object: Introduced
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Arrays;
import java.util.StringJoiner;
public class RootData {
public final AbstractData[] data;
#JsonCreator
public RootData(#JsonProperty("data") final AbstractData[] data) {
this.data = data;
}
#Override
public String toString() {
return new StringJoiner(", ", RootData.class.getSimpleName() + "[", "]")
.add("data=" + Arrays.toString(data))
.toString();
}
}
AbstractData data class: Updated to deserialize type property
Please, see the Javadoc:
Note on visibility of type identifier: by default, deserialization (use during reading of JSON) of type identifier is completely handled by Jackson, and is not passed to deserializers. However, if so desired, it is possible to define property visible = true in which case property will be passed as-is to deserializers (and set via setter or field) on deserialization.
Updated annotation by adding visible = true:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type",
visible = true,
defaultImpl = DefaultData.class
)
Additionally, please, see the related question: java - Jackson - #JsonTypeInfo property is being mapped as null?.
Data classes: Implemented toString() method
(Omitted.)
Main: Updated to use root data class
Please, note that I have corrected the JSON document: added the missing comma after "type": "type1".
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String... args) throws JsonProcessingException {
final String jsonDocumentString =
"""
{
"data": [{
"id": "id1",
"type": "type1",
"name": "John"
},
{
"id": "id2",
"type": "type2",
"name": "Rebecca"
},
{
"id": "id3",
"type": "unknown",
"name": "Peter"
}]
}
""";
final ObjectMapper mapper = new ObjectMapper();
final RootData rootData = mapper.readValue(jsonDocumentString, RootData.class);
System.out.println(rootData);
}
}
The program completes successfully, i.e. without an exception being thrown.
The program outputs:
RootData[data=[Type1Data[id='id1', type='type1', name='John'], Type2Data[id='id2', type='type2', name='Rebecca'], AbstractData[id='id3', type='unknown', name='Peter']]]
The actual result (output) is the same as the expected result.

Jackson deserialize JSON into pojo with map property

Can somebody help me, how I can deserialize the following JSON, which I can not change?
I am using Jackson for serialization.
{
"columns": [
{
"header": "Heading1",
},
{
"header": "Heading2",
}
],
"rows": [
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
]
}
Columns can have unknown number of headers and their value eg. "Header1" is used in the rows array.
So far I have the following structure:
public class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
public class ColumnConfig {
private String header;
}
public class QueryResultRow {
private int id;
private Map<String, CellValue> values;
}
public class CellValue{
private String value;
}
The problem is that the Map is empty when I deserialize into QueryResult;
I read about TypeReference but I do not know how I can specify a TypeReference<HashMap<String,CellValue>> for the property values in QueryResultRow.
Edit:
My ObjectMapper code is the following:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String queryResultString = loadQuery(queryPath);
QueryResult result = mapper.readValue(queryResultString, QueryResult.class);
The content of queryResultString is the JSON above.
First problem is your JSON is invalid. I assume it should be something like this,
{
"columns": [
{
"header": "Heading1"
},
{
"header": "Heading2"
}
],
"rows": [
{
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
}
]
}
Then answer is quite straightforward. You need to change your QueryResultRow as follows,
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
Then I think you should good to go.
Here is a complete working example,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"columns\":[{\"header\":\"Heading1\"},{\"header\":\"Heading2\"}],\"rows\":[{\"id\":1,\"Heading1\":{\"value\":\"Value1\"},\"Heading2\":{\"value\":\"Value2\"}}]}";
ObjectMapper om = new ObjectMapper();
QueryResult queryResult = om.readValue(s, QueryResult.class);
System.out.println(queryResult);
}
}
#Getter
#Setter
#ToString
class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
#Getter
#Setter
#ToString
class ColumnConfig {
private String header;
}
#Getter
#Setter
#ToString
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
#Getter
#Setter
#ToString
class CellValue{
private String value;
}

How to parse nested JSON object in java

Here is the sample JSON string. I want to parse this nested JSON object even though nested object have the same name. Some time we may have multiple levels of the nested objects. I tired with Jackson nested objects parsing but that did not work for me. After parsing the object, i want to convert that into a different format.Please help me in parsing this JSON. Thanks in advance.
{
"operator": "and",
"predicates": [
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "equal",
"value": "xxxx"
},
{
"columnName": "Age",
"datatype": "number",
"input": "number",
"operand": "greater_or_equal",
"value": "21"
}
]
},
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "not_equal",
"value": "nnn"
},
{
"columnName": "Birthday",
"datatype": "date",
"input": "date_picker",
"operand": "in",
"value": "2020-07-23,2020-07-24"
}
]
}
]
}
below is the code in java
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Predicates {
private String columnName;
private String datatype;
private String input;
private String operator;
private String value;
private String operand;
/**
*
*/
private List<Predicates> predicates;
#JsonProperty("predicates")
private void unpackNested(Map<String,Object> predicates) {
this.columnName = (String)predicates.get("columnName");
this.datatype = (String)predicates.get("datatype");
this.input = (String)predicates.get("input");
this.operator = (String)predicates.get("operator");
this.value = (String)predicates.get("value");
this.operand = (String)predicates.get("operand");
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDatatype() {
return datatype;
}
public void setDatatype(String datatype) {
this.datatype = datatype;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getOperand() {
return operand;
}
public void setOperand(String operand) {
this.operand = operand;
}
public List<Predicates> getPredicates() {
return predicates;
}
public void setPredicates(List<Predicates> predicates) {
this.predicates = predicates;
}
}
Parsing
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
I don't know what you are trying to achieve with your unpackNested method. Jackson can already bind properties from your JSON to your objects and handles recursive properties just fine.
I simply removed your unpackNested method and ran your code on your provided input:
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
The object pr contains the full json including the nested child predicates. (I ran the code with jackson databind 2.11.2).
If your properties are not auto-detected, annotate your getters with #JsonProperty:
class Predicate {
// ..snip..
#JsonProperty("input")
public String getInput() {
return input;
}
#JsonProperty("predicates")
public List<Predicates> getPredicates() {
return predicates;
}
}
But apart from that, no extra steps need to be taken. Jackson can already unpack nested objects, just remove your (weird) unpackNested method and let Jackson do its job.

Deserialize Json to java object

I am using Lombok to create builder pattern for my java object and I want to convert json into this object but it keeps saying cannot deserialize object.
#Builder
#Getter
#Setter
#Value
#NoArgConstructor
#JsonDeserialize()
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class SaleInfo {
private String country;
private String saleability; //This
private boolean isEbook; //THis
// created only if required
private String buyLink;
//TODO i want to have a conditional creation of this two objects below.
ListPrice listPrice;
RetailPrice retailPrice;
#Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
This is my code for using object mapper
public NodeBean readFromJsonFile() throws IOException {
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("book.json");
JsonNode node = objectMapper.readTree(resourceAsStream);
NodeBean nodebean = objectMapper.readerFor(NodeBean.class).readValue(node);
return nodebean;
}
It works fine when I implemented getters and setters and remove all lombok annotations, but I need it as a builder pattern, it's important (due to some optional objects like Listprice).
I am getting the error when i try to deserialize
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.valentine.cognifide_task.model.SaleInfo` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.valentine.cognifide_task.model.NodeBean["items"]->java.util.ArrayList[0]->com.valentine.cognifide_task.model.Item["saleInfo"])
This is the json , The other parts of the json i have the objects and they are ok only the saleInfo has issues as the field are changing sometimes it has Listprice and sometimes not, SO i want a builder class that will only create it if needed.
{
"kind": "books#volume",
"id": "UEdjAgAAQBAJ",
"etag": "/KAuiIWJuB4",
"selfLink": "https://www.googleapis.com/books/v1/volumes/UEdjAgAAQBAJ",
"volumeInfo": {
"title": "Java. Podstawy. Wydanie IX",
"authors": [
"Cay S. Horstmann",
"Gary Cornell"
],
"publisher": "Helion",
"publishedDate": "2013-12-09",
"description": "Kolejne wydanie tej cenionej książki zostało zaktualizowane o wszystkie nowości, które pojawiły się w wersji 7 platformy Java Standard Edition. W trakcie lektury poznasz składnię języka oraz wszystkie istotne kwestie związane z programowaniem w Javie. Zrozumiesz założenia programowania obiektowego, nauczysz się korzystać z interfejsów oraz obsługiwać wyjątki. Przekonasz się również, jakie ułatwienia w tym zakresie oferuje Java 7 obsługa wielu wyjątków w ramach jednego bloku catch to tylko czubek góry lodowej.",
"industryIdentifiers": [
{
"type": "ISBN_13",
"identifier": "9788324677610"
},
{
"type": "ISBN_10",
"identifier": "8324677615"
}
],
"readingModes": {
"text": true,
"image": true
},
"pageCount": 864,
"printType": "BOOK",
"categories": [
"Computers"
],
"maturityRating": "NOT_MATURE",
"allowAnonLogging": true,
"contentVersion": "2.5.4.0.preview.3",
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=UEdjAgAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=UEdjAgAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
},
"language": "pl",
"previewLink": "http://books.google.pl/books?id=UEdjAgAAQBAJ&pg=PA852&dq=java&hl=&cd=4&source=gbs_api",
"infoLink": "https://play.google.com/store/books/details?id=UEdjAgAAQBAJ&source=gbs_api",
"canonicalVolumeLink": "https://market.android.com/details?id=book-UEdjAgAAQBAJ"
},
"saleInfo": {
"country": "PL",
"saleability": "FOR_SALE",
"isEbook": true,
"listPrice": {
"amount": 79.0,
"currencyCode": "PLN"
},
"retailPrice": {
"amount": 55.3,
"currencyCode": "PLN"
},
"buyLink": "https://play.google.com/store/books/details?id=UEdjAgAAQBAJ&rdid=book-UEdjAgAAQBAJ&rdot=1&source=gbs_api",
"offers": [
{
"finskyOfferType": 1,
"listPrice": {
"amountInMicros": 7.9E7,
"currencyCode": "PLN"
},
"retailPrice": {
"amountInMicros": 5.53E7,
"currencyCode": "PLN"
}
}
]
},
"accessInfo": {
"country": "PL",
"viewability": "PARTIAL",
"embeddable": true,
"publicDomain": false,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": true,
"acsTokenLink": "http://books.google.pl/books/download/Java_Podstawy_Wydanie_IX-sample-epub.acsm?id=UEdjAgAAQBAJ&format=epub&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api"
},
"pdf": {
"isAvailable": true,
"acsTokenLink": "http://books.google.pl/books/download/Java_Podstawy_Wydanie_IX-sample-pdf.acsm?id=UEdjAgAAQBAJ&format=pdf&output=acs4_fulfillment_token&dl_type=sample&source=gbs_api"
},
"webReaderLink": "http://play.google.com/books/reader?id=UEdjAgAAQBAJ&hl=&printsec=frontcover&source=gbs_api",
"accessViewStatus": "SAMPLE",
"quoteSharingAllowed": false
},
"searchInfo": {
"textSnippet": "MF, 512 manifestu klasa główna, 514 sekcja główna, 512 wstawianie sekcji, 518 \u003cbr\u003e\nzmienianie zawartości, 513 MapTest.\u003cb\u003ejava\u003c/b\u003e,699 MenuFrame.\u003cb\u003ejava\u003c/b\u003e, 442 \u003cbr\u003e\nMethodTableTest.\u003cb\u003ejava\u003c/b\u003e,266 MouseComponent.\u003cb\u003ejava\u003c/b\u003e, 383 MouseFrame.\u003cb\u003ejava\u003c/b\u003e, 383 \u003cbr\u003e\nNotHelloWorld.\u003cb\u003ejava\u003c/b\u003e, 330, 534 ObjectAnalyzerTest.\u003cb\u003ejava\u003c/b\u003e,259 OptionDialogFrame.\u003cbr\u003e\n\u003cb\u003ejava\u003c/b\u003e,477 overview.html, 194 PackageTest.\u003cb\u003ejava\u003c/b\u003e, 184 PairTest1.\u003cb\u003ejava\u003c/b\u003e, 631 \u003cbr\u003e\nPairTest2.\u003cb\u003ejava\u003c/b\u003e, 634 PairTest3.\u003cb\u003ejava\u003c/b\u003e, 656 ParamTest.\u003cb\u003ejava\u003c/b\u003e, 169 PasswordChooser.\u003cbr\u003e\n\u003cb\u003ejava\u003c/b\u003e, 492 Person.\u003cb\u003ejava\u003c/b\u003e ..."
}
},
It seems like you are missing a constructor with the #JsonCreator tag and the #JsonProperty tags on the parameters.
I am working on a project right now where I use this in a jax-rs context.
Looking into it, it seems like Jackson might not actually use the required property though.
#Builder
#Getter
#Setter
#Value
#RequiredArgsConstructor
#JsonDeserialize()
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class SaleInfo {
private String country;
private String saleability; //This
private boolean isEbook; //THis
// created only if required
private String buyLink;
//TODO i want to have a conditional creation of this two objects below.
ListPrice listPrice;
RetailPrice retailPrice;
#JsonCreator
public SaleInfo(
#JsonProperty("country") String country;
#JsonProperty("saleability") String saleability;
#JsonProperty("isEbook") boolean isEbook;
#JsonProperty(value = "buyLink", required = false) String buyLink;
#JsonProperty(value = "listPrice", required = false) ListPrice listPrice;
#JsonProperty(value = "retailPrice", required = false) RetailPrice retailPrice;
) {
this.country = country;
this.saleability = saleability;
this.isEbook = isEbook;
this.buyLink = buyLink;
this.listPrice = listPrice;
this.retailPrice = country;
}
#Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}

How to make a service method that gets collection of POJOs

I have such service:
public interface FireService {
void addTags(String sessionId, List<TagCreateRequest> tags);
}
Here TagCreateRequest is:
#MetaClass(name = "...")
public class TagCreateRequest extends AbstractNotPersistentEntity implements Serializable {
#MetaProperty(mandatory = true)
protected TagType type;
#MetaProperty(mandatory = true)
protected Double time;
#MetaProperty
protected String text;
public void setType(TagType type) {
this.type = type;
}
public TagType getType() {
return type;
}
public void setTime(Double time) {
this.time = time;
}
public Double getTime() {
return time;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
My problem is when i'm trying to make REST request to method addTags like this:
http://localhost:8080/app/rest/v2/services/fire_FireService/addTags
{
"sessionId": "1417270d-31cb-be3c-e583-4b172b4183a9",
"tags": [
{
"type": "fire",
"time": 12.333
},
{
"type": "text",
"time": 15.12,
"text": "Test!!!"
}
]
}
I'm getting the EntitySerializationException that tells me that MetaClass for entity is not defined:
EntitySerializationException: Cannot deserialize an entity. MetaClass is not defined
I tried to look how platform determines the MetaClass and found strange thing. If service parameter is Collection, then passed MetaClass is null:
#Component("cuba_RestParseUtils")
public class RestParseUtils {
...
public Object toObject(Class clazz, String value) throws ParseException {
...
if (Collection.class.isAssignableFrom(clazz)) {
return entitySerializationAPI.<Entity>entitiesCollectionFromJson(value, null);
}
...
}
...
}
What should i do in this case?
You must explicitly specify the type of instances in the collection. Use the _entityName field in each TagCreateRequest entity:
{
"sessionId": "1417270d-31cb-be3c-e583-4b172b4183a9",
"tags": [
{
"_entityName": "prj_TagMetaClassName",
"type": "fire",
"time": 12.333
},
{
"_entityName": "prj_TagMetaClassName",
"type": "text",
"time": 15.12,
"text": "Test!!!"
}
]
}

Categories