I have new requirement, I am creating REST API which has dynamic request (actions) and I want to convert that JSON request to POJO, I know how to convert JSON to POJO where key's are same, but not sure what to do when there are different content on objects.
My Json is as follow.
{
"name":"Workflow",
"actions": [
{
"name": "EDIT_PROPERTY",
"payload": {
"name": "city",
"value": "Pune"
}
},
{
"name":"SEND_EMAIL",
"payload":{
"from":"no-reply#yaho.com",
"to":"alpesh#yahoo.com",
"subject":"Try email",
"body":"content"
}
},
{
"name":"CREATE_TASK",
"payload":{
"user":1,
"type":"call",
"status":"open",
"note":"This is note content"
}
}
]
}
As you can see actions are set of Objects which has name and payload, now payload has different fields, I have predefined names. and each payload under action has predefined keys as you see.
I want to convert this to POJO something like
class Workflow{
String name;
Set<Action> actions;
}
class Action {
String name;
//What to add as payload
}
Thanks
Alpesh
This is what you can do :
JSON to POJO model :
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Event {
public String name;
public List<Action> actions;
#Data
public static class Action {
public String name;
Map<String, Object> payload;
}
}
public class TestJson {
private static String json = "{\n" +
" \"name\":\"Workflow\",\n" +
" \"actions\": [\n" +
" {\n" +
" \"name\": \"EDIT_PROPERTY\",\n" +
" \"payload\": {\n" +
" \"name\": \"city\",\n" +
" \"value\": \"Pune\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"name\":\"SEND_EMAIL\",\n" +
" \"payload\":{\n" +
" \"from\":\"no-reply#yaho.com\",\n" +
" \"to\":\"alpesh#yahoo.com\",\n" +
" \"subject\":\"Try email\",\n" +
" \"body\":\"content\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"name\":\"CREATE_TASK\",\n" +
" \"payload\":{\n" +
" \"user\":1,\n" +
" \"type\":\"call\",\n" +
" \"status\":\"open\",\n" +
" \"note\":\"This is note content\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
#SneakyThrows
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Event event = objectMapper.readValue(json, Event.class);
System.out.println(event);
}
}
When debugging, you'll notice that our objects have been filled accordingly:
Well, generally, I prefer any solution that does not involve Component Annotations. This is my own personal preference because eliminating constructors and method parameters is usually a giant headache. This is based on 1999 - 2006 Java Programming experience. If you have a need to dynamically generate classes and constructors or getters, then you may easily ignore or delete this answer. For me, JSON Parsing is practice right now.
In this posted answer, I have used the older Java JSON Library whose JavaDoc may be viewed here: javax.json.*. Here is my solution. It requires / expects that you write:
Your own toString() methods for your JSON Data Classes
Your own retrieve operations
The following code has output, and that is included at the end of this post. Usually I include a lot of Code Documentation. However, when the code is strictly parsing data, the code itself is usually so legible that more comments would clutter the retrieve and the toString() operations, so I have left them as is.
import java.util.*;
import javax.json.*;
import java.io.*;
public class Messages
{
public abstract static class Action
{
public final String name;
public Action(String name) { this.name=name; }
}
public static class EditProperty extends Action
{
public final String propName, propValue;
public EditProperty(String name, String value)
{ super("EDIT_PROPERTY"); this.propName=name; this.propValue=value; }
public String toString()
{
return
"Action: " + name + '\n' +
"Name: " + propName + '\n' +
"Value: " + propValue + '\n';
}
}
public static class SendEmail extends Action
{
public final String from, to, subject, body;
public SendEmail(String from, String to, String subject, String body)
{ super("SEND_EMAIL"); this.from=from; this.to=to; this.subject=subject; this.body=body; }
public String toString()
{
return
"Action: " + name + '\n' +
"From: " + from + '\n' +
"To: " + to + '\n' +
"Subject: " + subject + '\n' +
"Body: " + body + '\n';
}
}
public static class CreateTask extends Action
{
public final int user;
public final String type, status, note;
public CreateTask(int user, String type, String status, String note)
{ super("CREATE_TASK"); this.user=user; this.type=type; this.status=status; this.note=note; }
public String toString()
{
return
"Action: " + name + '\n' +
"User: " + user + '\n' +
"Type: " + type + '\n' +
"Status: " + status + '\n' +
"Note: " + note + '\n';
}
}
public static void main(String[] argv) throws IOException
{
Vector<Action> actions = new Vector<>();
Reader r = new FileReader("in.json");
JsonArray actionList = Json
.createReader(r)
.readObject()
.getJsonArray("actions");
for (JsonObject actionObj : actionList.getValuesAs(JsonObject.class))
{
JsonObject payload = actionObj.getJsonObject("payload");
Action action = null;
switch (actionObj.getString("name"))
{
case "EDIT_PROPERTY" : action = new EditProperty(
payload.getString("name"),
payload.getString("value")
); break;
case "SEND_EMAIL" : action = new SendEmail(
payload.getString("from"),
payload.getString("to"),
payload.getString("subject"),
payload.getString("body")
); break;
case "CREATE_TASK" : action = new CreateTask(
payload.getInt("user"),
payload.getString("type"),
payload.getString("status"),
payload.getString("note")
); break;
}
actions.add(action);
}
for (Action action : actions) System.out.println(action);
}
}
The class and inner-classes above would produce this output when invoked at the command line:
#cloudshell:~$ java Messages
Action: EDIT_PROPERTY
Name: city
Value: Pune
Action: SEND_EMAIL
From: no-reply#yaho.com
To: alpesh#yahoo.com
Subject: Try email
Body: content
Action: CREATE_TASK
User: 1
Type: call
Status: open
Note: This is note content
The JSON you show is actually a list of one object type;
specifically, the payload is just a Map of String to Object.
Once you parse the JSON,
your code will need to process each "different" payload type
based on the payload type.
Here is some sample code:
public class BlamMessage
{
private String name;
private Map<String, Object> payload;
...
}
public class MessageHolder
{
#JsonProperty("actions")
private List<BlamMessage> messageList;
private String name;
...
}
Here is my final class "Constants"
#Component
public final class Constants {
#Value("${db2.schema}")
private static String Schema;
public static final String STUDENT_TABLE = Schema + ".Student";
}
I have db2.schema in my properties file :
db2.schema = ${DB2_SCHEMA}
DB2_SCHEMA = D5677ESB
#Value can not be used with static fields
You can use like:
...
public class Constants {
public static final String NewOrder = "neworder";
public static final String POST = "POST";
public static final String CONTENT_TYPE = "Content-Type";
public static final String APPLICATION_TYPE = "application/json";
public static final String ACCEPT = "Accept";
public static final String CART_URL = PropsUtil.get("order.inquiry.search.insertCartDataURL");
}
...
** or check this code:**
...
public String getPropValues() throws IOException {
try {
Properties prop = new Properties();
String propFileName = "config.properties";
inputStream = getClass().getClassLoader().getResourceAsStream(propFileName);
if (inputStream != null) {
prop.load(inputStream);
} else {
throw new FileNotFoundException("property file '" + propFileName + "' not found in the classpath");
}
Date time = new Date(System.currentTimeMillis());
// get the property value and print it out
String user = prop.getProperty("user");
String company1 = prop.getProperty("company1");
String company2 = prop.getProperty("company2");
String company3 = prop.getProperty("company3");
result = "Company List = " + company1 + ", " + company2 + ", " + company3;
System.out.println(result + "\nProgram Ran on " + time + " by user=" + user);
} catch (Exception e) {
System.out.println("Exception: " + e);
} finally {
inputStream.close();
}
return result;
} ...
Here is my json URL https://jsonplaceholder.typicode.com/todos I want to display only completed: true is to be strike through, how can I do that?
MainActivity.java
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceHolderApi jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Call<List<Post>> call = jsonPlaceHolderApi.getPosts();
call.enqueue(new Callback<List<Post>>() {
#Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
if (!response.isSuccessful()) {
textresult.setText("Code: " + response.code());
return;
}
List<Post> posts = response.body();
for (Post post : posts) {
String content = "";
content += "User ID: " + post.getUserId() + "\n";
content += "ID: " + post.getId() + "\n";
content += " Title: " + post.getTitle() + "\n";
content += "Completed: " + post.getCompleted() + "\n\n";
textresult.append(content);
if (post.getCompleted().contains("true")) {
textresult.setPaintFlags(textresult.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
}
}
#Override
public void onFailure(Call<List<Post>> call, Throwable t) {
textresult.setText(t.getMessage());
}
});
JsonPlaceHolderApi.java
public interface JsonPlaceHolderApi {
#GET("todos")
Call<List<Post>> getPosts();
}
Post.java
public class Post {
private int userId;
private int id;
private String title;
private String completed;
public int getUserId() {
return userId;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public String getCompleted() {
return completed;
}
}
When I try to run the above code I got output as image, but I need if completed: true it should be strike.
it is not possible to do setPaintFlags() with custom text better use can use HTML text for your work for you don't need to check "post.complete()", just append all content in a single string and replace your target string (complete: true) with HTML text.
just do it like
String content = "";
for (Post post : posts) {
content += "User ID: " + post.getUserId() + "\n";
content += "ID: " + post.getId() + "\n";
content += " Title: " + post.getTitle() + "\n";
content += "Completed: " + post.getCompleted() + "\n\n";
}
String tempHtmlText = content.replaceAll("Completed: true","<strike>Completed: true</strike>");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textresult.setText(Html.fromHtml(tempHtmlText , Html.FROM_HTML_MODE_LEGACY));
} else {
textresult.setText(Html.fromHtml(tempHtmlText));
}
or you can use Gson formmater
I'm having a problem converting a XML SOAP return to a corresponding POJO class. The XML return looks like this:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header></env:Header>
<env:Body>
<ns2:teste xmlns:ns2="http://teste.com.br/">
<retorno>
<codigoRetorno>000</codigoRetorno>
<descricao>Consulta Realizada com Sucesso</descricao>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
</retorno>
</ns2:teste >
</env:Body>
</env:Envelope>
I tried to use the Jackson XMLmapper, but I can not get it to consider the 'RETURN' node as the ROOT element during deserialization. it considers the 'Envelope' node as ROOT node.
I need to extract just the return node and convert to my pojo class.
Another problem is that 'item' nodes should be part of a collection, however there is no parent node grouping these elements.
Does anyone know of a parser that does deserialization of this type of xml?
You can incorporate a streaming XML Parser (StAX) and XmlMapper in this way:
import java.io.StringReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class Deser {
// #formatter:off
private static final String JSON = " <env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <env:Header></env:Header>\n" +
" <env:Body>\n" +
" <ns2:teste xmlns:ns2=\"http://teste.com.br/\">\n" +
" <retorno>\n" +
" <codigoRetorno>000</codigoRetorno>\n" +
" <descricao>Consulta Realizada com Sucesso</descricao>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" </retorno>\n" +
" </ns2:teste >\n" +
" </env:Body>\n" +
"</env:Envelope>";
// #formatter:on
private static final String TARGET_ELEMENT = "retorno";
public static void main(String[] args) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
XMLInputFactory f = XMLInputFactory.newFactory();
XMLStreamReader sr = f.createXMLStreamReader(new StringReader(JSON));
while (sr.hasNext()) {
int type = sr.next();
if (type == XMLStreamReader.START_ELEMENT && TARGET_ELEMENT.equals(sr.getLocalName())) {
Retorno r = xmlMapper.readValue(sr, Retorno.class);
System.out.println(r.getDescricao());
}
}
}
}
class Retorno {
private int codigoRetorno;
private String descricao;
public int getCodigoRetorno() {
return codigoRetorno;
}
public void setCodigoRetorno(int codigoRetorno) {
this.codigoRetorno = codigoRetorno;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
This yields:
Consulta Realizada com Sucesso
Adapt the code as necessary, this is just to prove out how to get it to do what you need!
The cleanest solution I found was using JSOUP:
private <T> T parseResponse(HttpEntity entity, Class<T> typeTarget) throws Exception {
try {
String xmlSoapResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);
String xmlRetorno = extractXmlElement(xmlSoapResponse, "retorno");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
xmlMapper.registerModule(new JavaTimeModule());
return xmlMapper.readValue(xmlRetorno.toString(), typeTarget);
} catch (Exception e) {
throw new Exception("Fail during parser", e);
}
}
private String extractXmlElement(String xmlString, String nodeTagNameElement) {
Document document = Jsoup.parse(xmlString, "", Parser.xmlParser());
document.outputSettings().prettyPrint(false);
Elements retorno = document.getElementsByTag(nodeTagNameElement);
return retorno.toString();
}
I'm having a little trouble working out an appropriate java object structure for the following JSON data:
"pages": {
"181382": {
"pageid": 181382,
"ns": 0,
"title": "Anonymity"
},
"7181837": {
"pageid": 7181837,
"ns": 0,
"title": "Anonymous"
}
}
The identifiers "181382" and "7181837" change depending on the data returned so these cannot be used as a member on an object. I tried to approach it using a Map<String, Object> approach but got a little stuck.
Edit:
This is what I've tried
public class PageData {
int pageid;
String ns;
String title;
public int getPageid() {
return pageid;
}
public String getNs() {
return ns;
}
public String getTitle() {
return title;
}
}
Map<String, PageData> pages = results.getPages().getData();
for (PageData data : pages.values()) {
System.out.println(data.getTitle());
}
Just create some wrapper for your Object. Here is working example:
Wrapper
public class Wrapper {
Map<String, PageData> pages = null;
public Map<String, PageData> getPages() {
return pages;
}
}
Launcher
public class Launcher {
public static void main(String[] args) {
String str = "{\"pages\": {\r\n" +
" \"181382\": {\r\n" +
" \"pageid\": 181382,\r\n" +
" \"ns\": 0,\r\n" +
" \"title\": \"Anonymity\"\r\n" +
" },\r\n" +
" \"7181837\": {\r\n" +
" \"pageid\": 7181837,\r\n" +
" \"ns\": 0,\r\n" +
" \"title\": \"Anonymous\"\r\n" +
" }\r\n" +
" }" +
"}";
Gson gson = new Gson();
Wrapper results = gson.fromJson(str, Wrapper.class);
Map<String, PageData> pages = results.getPages();
for (PageData data : pages.values()) {
System.out.println(data.getTitle());
}
}
}
PageData
public class PageData{/* the same */}
Output:
Anonymity
Anonymous