Parsing Yaml in Java - java

I have the following YAML I want to parse using Jackson parser in Java.
android:
"7.0":
- nexus
- S8
"6.0":
- s7
- g5
ios:
"10.0":
- iphone 7
- iphone 8
I created a created class which has getter and setter as Java Object for android. It works fine. But how do I do the same for 6.0 and 7.0? I'm usingJackson` Parser

No idea whether Jackson supports that; here's a solution with plain SnakeYaml (I will never understand why people use Jackson for parsing YAML when all it does is basically take away the detailed configuration possible with SnakeYaml which it uses as backend):
class AndroidValues {
// showing what needs to be done for "7.0". "8.0" works similarly.
private List<String> v7_0;
public List<String> getValuesFor7_0() {
return v7_0;
}
public void setValuesFor7_0(List<String> value) {
v7_0 = value;
}
}
// ... in your loading code:
Constructor constructor = new Constructor(YourRoot.class);
TypeDescription androidDesc = new TypeDescription(AndroidValues.class);
androidDesc.substituteProperty("7.0", List.class, "getValuesFor7_0", "setValuesFor7_0");
androidDesc.putListPropertyType("7.0", String.class);
constructor.addTypeDescription(androidDesc);
Yaml yaml = new Yaml(constructor);
// and then load the root type with it
Note: Code has not been tested.

I think that you should try annotation com.fasterxml.jackson.annotation.JsonProperty. I'll provide a short example below.
Sample YAML file:
---
"42": "some value"
Data transfer object class:
public class Entity {
#JsonProperty("42")
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Parser:
public class Parser {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Entity entity = mapper.readValue(new File("src/main/resources/sample.yml"), Entity.class);
System.out.println(entity.getValue());
}
}
The console output should be: some value.
P.S. I tested it with the following dependencies:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>

Related

How to serialise a POJO to be sent over a Java RSocket?

I'm trying to send a POJO over an RSocket requestStream:
import java.io.Serializable;
class GreetingRequest implements Serializable {
private String name;
public GreetingRequest() {}
public GreetingRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If I were to send a String I can do:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes("Hello".getBytes());
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
But how can I serialise my POJO?
This is my attempt using implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' which doesn't work:
GreetingRequest pojo = new GreetingRequest("Davide");
ByteBuf data = SerializationUtils.serialize(pojo);
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
There is a native java serialization mechanism that I would NOT recommend, but you can read about it. Read about Serialazable interface in Java API. There are 2 options that I would recommend:
JSON-JACKSON (also known as Faster XML)
GSON (mentioned in the answer from César Ferreira)
Both convert classes to JSON and vise-versa. For JSON-JACKSON see class ObectMapper. In particular methods writeValueAsString() or writeValueAsBytes() to serialize your object to JSON string or bytes. And to convert it back look for method readValue().
Here are the Maven artifacts that you would need to use it:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.3</version>
</dependency>
I recommend you using Gson converter. It helps you to convert a Java Class to a JSON String. And then you can work with the String as if you were working with simple text.
You can import the dependency:
dependencies {
implementation 'com.google.code.gson:gson:2.8.7'
}
And then, can use jsonschema2pojo to convert the JSON:
{ "name": "Test" }
to classes like this:
package com.example;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class GreetingRequest {
#SerializedName("name")
#Expose
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
After all is done you can do something like this in Java:
Gson converter = new Gson();
GreetingsRequest request = new GreetingRequest();
request.setName("Test");
String greetingsJSON = converter.toJson(request);
And then you can still send the JSON string as it follows:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes(greetingsJSON.getBytes());
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
Data conversions:
JSON Object - Java class
Array - List<>
Helpful links:
This is the library you need to include (tutorials included) in Java: GSON Converter Git
This is an JSON to Class online converter: Jsonschema2pojo generator
If you use a framework like Spring Boot this is taken care of for you. You may want to manually control in which case the other examples are more relevant, but there are productivity benefits to Spring Boot or rsocket-rpc.
https://github.com/rsocket/rsocket-demo/blob/master/src/main/kotlin/io/rsocket/demo/chat/ChatController.kt
https://spring.io/blog/2020/03/02/getting-started-with-rsocket-spring-boot-server
or rsocket-rpc-java using protobuf instead of Serialization
https://github.com/rsocket/rsocket-rpc-java/blob/master/docs/get-started.md

Deserializing into class with specified key from response in jackson

Im getting response from external api
"success": true,
"data": [
{}
I'd like to map only data and it's corresponding array as entire class.
Right now I have wrapper for it but it is +1 class just for that.
public class YYYYYY {
private boolean success;
#JsonProperty(value = "data")
private List<PipeDriveContact> arrayData;
This is similar to https://stackoverflow.com/a/19097149/6785908
You'll first need to get the array
String jsonStr = "{\"success\": true,\"data\": [{\"test\": \"some data\"}]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonStr);
ArrayNode arrayNode = (ArrayNode) node.get("data");
System.out.println(arrayNode);
List<PipeDriveContact> pojos = mapper.readValue(arrayNode.toString(), new TypeReference<List<PipeDriveContact>>() {});
System.out.println(pojos);
prints (with a toString())
[{\"test\": \"some data\"}] // the json array
But trust me, unless you have a very compelling reason (than "I don't want one more class"), I would discourage you from heading down this path, instead implement it with the wrapper class and call it done.
Reason: In future you may generate your Pojos from a contract (swagger spec / ol JSON schema), or you may find some use for the "success" field.
If you absolutely don't need the other keys in the outermost object, you could parse out the array against the key "data" and then parse it separately into your POJO. Following is my rough implementation:
First, parse out the data array:
String json = "{\"success\": true,\"data\": [{\"test\": \"some data\"}]}";
JSONObject obj = new JSONObject(json);
String data = obj.getJSONArray("data").toString();
Then, using Jackson (or anything else), create an ArrayList with your required objects:
ObjectMapper objectMapper = new ObjectMapper();
TypeReference<ArrayList<PipeDriveContact>> typeRef = new TypeReference<ArrayList<PipeDriveContact>>() {};
ArrayList<PipeDriveContact> dataArray = objectMapper.readValue(data, typeRef);
Following is the model POJO I created for testing:
public class PipeDriveContact {
private String test;
public String getTest() { return test; }
public void setTest(String test) { this.test = test; }
}
Following are the dependencies I used:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
Hope this helps.
Either you can use #JsonCreator for defining constructor and populate only data
Or you can also use Custom de-serializer too.
Further reading and examples:
http://www.baeldung.com/jackson-annotations
http://buraktas.com/convert-objects-to-from-json-by-jackson-example/
https://codexplo.wordpress.com/2015/11/26/custom-json-deserialization-with-jackson/
Maybe it's not exactly what you are looking for, but you can map only the fields you need in your class with #JsonIgnoreProperties(ignoreUnknown = true) :
#JsonIgnoreProperties(ignoreUnknown = true)
public class YYYYYY {
#JsonProperty(value = "data")
private List<PipeDriveContact> arrayData;

Jackson xml module: deserializing immutable type with a #JacksonXmlText property

I want to serialize an immutable type both as json and as xml:
The serialized JSON is like:
{
"text" : "... the text..."
}
and the serialized xml is like:
<asText>_text_</asText>
(note that the text is the xml's element text)
The java object is like:
#JsonRootName("asText")
#Accessors(prefix="_")
public static class AsText {
#JsonProperty("text") #JacksonXmlText
#Getter private final String _text;
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
}
beware that the _text property is final (so the object is immutable) and it's annotated with #JacksonXmlText in order to be serialized as the xml element's text
Being the object immutable, a constructor from the text is needed and the constructor's argument must be annotated with #JsonProperty
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
When serializing / deserializing to/from JSON everything works fine
... the problem arises when serializing / deserializing to/from XML:
// create the object
AsText obj = new AsText("_text_");
// init the mapper
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
log.warn("Serialized Xml\n{}",xml);
// Read from xml
log.warn("Read from Xml:");
AsText objReadedFromXml = mapper.readValue(xml,
AsText.class);
log.warn("Obj readed from serialized xml: {}",
objReadedFromXml.getClass().getName());
The exception is:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class r01f.types.url.UrlQueryStringParam), not marked as ignorable (2 known properties: "value", "name"])
It seems that the xml module needs the object's constructor to be annotated like:
public AsText(#JsonProperty("") final String text) {
_text = text;
}
BUT this does NOT even works:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `test.types.SerializeAsXmlElementTextTest$AsText` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
The annotation #JsonProperty("text") at the constructor's argument is needed to deserialize from JSON
... how can i make this to work
Try adding a public getter for the property. I believe that should fix the deserialization issue.
#JsonRootName("asText")
#Accessors(prefix = "_")
public static class AsText {
#JsonProperty("text")
#JacksonXmlText
#Getter
private final String _text;
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
public String getText() {
return _text;
}
}
Actually, it works without adding a getter too, with these versions of Lombak & Jackson.
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
</dependencies>
I had same issue with error "no delegate- or property-based Creator". In my case it was problem with Immutables version 2.5.6. I have fixed it by downgrade to version 2.5.5. Version 2.5.6 is available in mvnrepository.com but on official page is as stable version marked 2.5.5.
Update 2018
This hack worked in 2.9.0 but it seems to stop working after this commit. It is not clear if there is an easy way to make it work again.
It looks like the main reason for your issue is that you try to use JSON and XML serialization at the same time but with different configurations. Unfortunately XmlMapper inherits from ObjectMapper and inherits all the JSON-specific configuration (and you can override it but can not clear it with XML-specific annotations) which is the reason for your conflict. It seems that the simplest way to work this around is to use #JsonAlias annotation in the constructor. It is a bit hacky but it works. Here is a minimal example (without Lombok) that works for me:
#JsonRootName("asText")
public static class AsText {
#JsonProperty("text")
#JacksonXmlText
private final String _text;
public AsText(#JsonAlias("") #JsonProperty("text") final String text) {
_text = text;
}
#JsonIgnore
public String getText() {
return _text;
}
#Override
public String toString() {
return "AsText{" +
"_text='" + _text + '\'' +
'}';
}
}
Note that I also added #JsonIgnore to the getter because else I didn't get XML format you requested (and you can do the same using Lombok's onMethod as described at onX).
For a simple test:
public static void main(String[] args) throws IOException {
// create the object
AsText obj = new AsText("123_text_");
// init the mapper
//ObjectMapper mapper = new ObjectMapper();
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
System.out.println("Serialized Xml\n" + xml);
// Read from xml
AsText objReadedFromXml = mapper.readValue(xml, AsText.class);
System.out.println("Read from Xml: " + objReadedFromXml);
}
I get the following output:
Serialized Xml
<asText>123_text_</asText>
Read from Xml: AsText{_text='123_text_'}

Jackson not populating all properties

I am working on a simple example using Jackson library to convert a json string back to Java object but I see only few properties are being set on my java object instead of all properties.
Here is my code:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.codehaus.jackson.map.ObjectMapper;
public class JsonTest {
public static void main(String[] args) throws FileNotFoundException, IOException {
StringBuffer buffer = new StringBuffer();
String data = "";
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("path-to-sample.json"));
while ((data = reader.readLine()) != null) {
buffer.append(data);
}
} finally {
if (reader != null) {
reader.close();
}
}
System.out.println(buffer.toString());
ObjectMapper mapper = new ObjectMapper();
Sample obj = mapper.readValue(buffer.toString(), Sample.class);
System.out.println(obj);
}
}
The Sample.java program looks like this:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Sample {
#JsonProperty("prop_1")
private String prop1;
private String prop2;
#JsonProperty("prop_3")
private String prop3;
private String prop4;
// Setters & Getters for the properties.
#Override
public String toString() {
return "Sample [prop1=" + prop1 + ", prop2=" + prop2 + ", prop3="
+ prop3 + ", prop4=" + prop4 + "]";
}
}
Input json string in my file is :
{
"prop_1": "1",
"prop2": "2",
"prop_3": "3",
"prop4": "4"
}
The output of this program is :
Sample [prop1=null, prop2=2, prop3=null, prop4=4]
As per my program the prop1 and prop3 should not be null. I am not clear where I made mistake.
Update:
If I remove the #JsonProperty annotation then I am getting the exception as :
Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "prop_1" (Class Sample), not marked as ignorable
This is my pom.xml file dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
You said in your comment, that you're using Jackson in version "2.5.4", but you're importing the ObjectMapper class from the org.codehaus package. This means, that this class is from version 1.9.13 (or from an older version).
I can reproduce your problem if I mix the versions using ObjectMapper and JsonIgnoreProperties from version 1.9.13 (org.codehaus) and JsonProperty from version 2.6.0 (com.fasterxml).
Output:
Sample [prop1=null, prop2=2, prop3=null, prop4=4]
If I only use version 1.9.13 or 2.6.0, then the result is ok:
Sample [prop1=1, prop2=2, prop3=3, prop4=4]
(for both)
So I recommend to make sure that you don't mix the used libraries and I recommend to use the newest version, which is from FasterXML.
But the used version is up to you.
You can download the jar file from here:
org.codehaus.jackson v1.9.13
com.fasterxml.jackson (core) v2.6.0
Btw about your comment:
#OldCurmudgeon, Thanks for responding. Changing the fields to public has not fixed the issue. I have removed the #JsonProperty annotation and then changed the setter methods to setProp_1 & setProp_3, it worked. So does it mean that there is an issue with #JsonProperty annotation?
Yes, you have (or hopefully had :P) a problem with that annotation: it was from a different Jackson version.
About your edit:
The link to the Jackson lib from fasterXML in the maven repository has one big advantage: it shows you which lib you should download to work with Jackson in your project.
You need:
Jackson Databind (which also has the ObjectMapper class)
Jackson Core
Jackson Annotations

How to easily and smoothly get data from data which should be of MediaType.JSON in Java?

Is it a nice practice to retrieve a JSON object as a String and then parse it manually inside the application or there is a better way to get a transfer object representation (eg. some tools or comfortable APIs, automated mapping services, don't know)?
Example:
#POST
#Path("/myUrlPath")
public Response postSomething(String jsonAsString) {
JSON json = getFromMyCustomParser(jsonAsString);
MyObject myObject = getFromMyCustomMapper(json);
//business logic
}
Don't know much about this topic.
You can actually accept a JSON as a parameter of your resource method. The Jersey REST API would support this. You might have to add a JSON library as a dependency.
I think that this is the JSON dependency we use:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.12</version>
</dependency>
In this case we use the JSONObject class from org.codehaus.jettison.json.
There's a tutorial on how to do this using the Jersey framework. It explains POJO mapping which is based on Jackson. You may have to configure POJO mapping yourself.
Heres an example for Jackson:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new Something("Name")));
public class Something {
private String name;
public Something(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Output: {"name":"Name"}
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.1</version>
</dependency>
Further reading: Jackson
Example for org.json.JSONObject:
JSONObject json = new JSONObject();
List<Object> list = new ArrayList<>();
list.add("Hello");
list.add("Hello2");
list.add("Hello3");
json.put("List", list);
System.out.println(json.toString());
Output: {"List":["Hello","Hello2","Hello3"]}
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
Further reading: JSON.org
Example for GSON
(https://sites.google.com/site/gson/gson-user-guide#TOC-Object-Examples)
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
Serialization
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
Deserialization
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
Output: {"value1":1,"value2":"abc"}
Further reading: GSON

Categories