How to execute Mustache template with JSON/String on Android? - java

I have String object with template base, something like:
<h1>{{header}}</h1>
{{#bug}}
{{/bug}}
{{#items}}
{{#first}}
<li><strong>{{name}}</strong></li>
{{/first}}
{{#link}}
<li>{{name}}</li>
{{/link}}
{{/items}}
{{#empty}}
<p>The list is empty.</p>
{{/empty}}
I want to pull another String object representing JSONObject and put its fields into template:
{
"header": "Colors",
"items": [
{"name": "red", "first": true, "url": "#Red"},
{"name": "green", "link": true, "url": "#Green"},
{"name": "blue", "link": true, "url": "#Blue"}
],
"empty": false
}
In the end I would get String representing HTML structure:
<h1>Colors</h1>
<li><strong>red</strong></li>
<li>green</li>
<li>blue</li>
I don't want to use any POJOs or Maps - only use standard String objects or alternatively convert second String into JSONObject to use it as a template's context.
Could someone give me any example how to achieve that?
Thanks.
Edit: I don't know anything about template/JSON structure while executing template - I have to play with unknown template/JSON and assume that they are correct.

I couldn't find way to work on pure String objects - I am converting JSONObject to Map to get it working with Mustache. This is code for conversion:
public static Map<String, Object> toMap(JSONObject object) throws JSONException
{
Map<String, Object> map = new HashMap();
Iterator keys = object.keys();
while (keys.hasNext())
{
String key = (String) keys.next();
map.put(key, fromJson(object.get(key)));
}
return map;
}
public static List toList(JSONArray array) throws JSONException
{
List list = new ArrayList();
for (int i = 0; i < array.length(); i++)
{
list.add(fromJson(array.get(i)));
}
return list;
}
private static Object fromJson(Object json) throws JSONException
{
if (json instanceof JSONObject)
{
return toMap((JSONObject) json);
} else if (json instanceof JSONArray)
{
return toList((JSONArray) json);
} else
{
return json;
}
}
Usage:
mustacheTemplate.execute(JSONUtils.toMap(new JSONObject(myString)));

Related

Json string to Object mapping with dynamic values

I am consuming Thirdparty jsonString, I am trying to parse the json but sometimes JSON object "RadarReports" is an list and sometimes it object.
{"RadarReports": {
"executionTime": "135",
"RadarReport": {
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
"size" :"1"
}}
=================
{"RadarReports": {
"executionTime": "113",
"RadarReport": [
{
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
{
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
]
"size" : "2"
}}
I tried below to parse but failing when single object came into picture, need to accept both single and list of objects.
#Data
public class Radarreports {
private int size;
private ArrayList<RadarreportSet> RadarReportSet;
private ArrayList<RadarReport> RadarReport;
}
#Data
public
class ReportsResponse {
Radarreports RadarReports;
}
URL url = new URL(queryUrl);
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
Gson gson = new GsonBuilder().create();
ReportsResponse radarReports = gson.fromJson(br, ReportsResponse.class);
You could solve this with a custom TypeAdapterFactory which creates an adapter which first peeks at the type of the JSON data and then adds special handling where the JSON object is not wrapped in an JSON array:
// Only intended for usage with #JsonAdapter
class SingleObjectOrListAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Note: Cannot use getDelegateAdapter due to https://github.com/google/gson/issues/1028
TypeAdapter<T> listAdapterDelegate = gson.getAdapter(type);
TypeAdapter<JsonObject> jsonObjectAdapter = gson.getAdapter(JsonObject.class);
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
listAdapterDelegate.write(out, value);
}
#Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.BEGIN_OBJECT) {
// Wrap JSON object in a new JSON array before parsing it
JsonObject jsonObject = jsonObjectAdapter.read(in);
JsonArray jsonArray = new JsonArray();
jsonArray.add(jsonObject);
return listAdapterDelegate.fromJsonTree(jsonArray);
} else {
return listAdapterDelegate.read(in);
}
}
};
}
}
The factory can then be specified for the affected field with #JsonAdapter:
#JsonAdapter(SingleObjectOrListAdapterFactory.class)
private ArrayList<RadarReport> RadarReport;

Get parent from nested Map - Java

I have this json Input
[
{
"id": "32",
"name": "Menu full"
"description": "Here is the object {id} is {name}"
},
{
"id": "60",
"name": "Side bar menu"
"description": "Here is the object {id}",
"children": [
{
"id": "60.1",
"name": ""
"description": "Here is the object {id} is {name}",
"children": []
}
]
}
]
Here is the java class representation.
public class Menu {
String id;
String name;
String description;
List <Menu> children;
//Getters and Setters
}
This is a recursive java object since the children is the same type as the parent.
I need to search and replace all variables inside "{}" with the value of the search by key using the value obtained from the string in {}. from the given json this should be the output.
Output
[
{
"id": "32",
"name": "Menu full"
"description": "Here is the object 32 is Menu full"
},
{
"id": "60",
"name": "Side bar menu"
"description": "Here is the object 60",
"children": [
{
"id": "60.1",
"name": ""
"description": "Here is the object 60.1 is Side bar menu",
"children": []
}
]
}
]
As you can see in the children there was no "{name}" in the current object to replace for. In that case the logic should get the "name" value from the key "name" obtained from the parent. If the parent dont have that value in the key (null) or is empty then the replace for the children should be String.empty
In order to achieve this I transformed the java object to a Map<String, Object> since I need to first determinate if the value has a replacement String ("{value}") and then search the key based on that replacement String.
Assuming I have already transformed the object to a Map.
private void ReplaceAllVariablesMap (Map<String, Object> objectMap) {
objectMap.entrySet()
.forEach(entry -> {
String value = Optional.ofNullable(entry.getValue()).orElse("").toString();
List<String> valuesToReplace = getValuesToReplace(value);
if(entry.getValue() instanceof String && valuesToReplace.size() > 0){
valuesToReplace.forEach(v -> {
String valueToReplaceWith = getValueByKey(objectMap, getValueFromBracketString(v));
value.replace(v, valueToReplaceWith);
});
}
if (entry.getValue() instanceof Map) {
Map<String, Object> map = (Map<String, Object>) entry.getValue();
ReplaceAllVariablesMap(map);
} else if (entry.getValue() instanceof List) {
List<?> list = (List<?>) entry.getValue();
list.forEach(listEntry -> {
if (listEntry instanceof Map) {
Map<String, Object> map = (Map<String, Object>) listEntry;
ReplaceAllVariablesMap(map);
}
});
}
});
}
private List<String> getValuesToReplace(String value){
return Pattern.compile("\\{(.*?)\\}")
.matcher(value)
.results()
.map(MatchResult::group)
.collect(Collectors.toList());
}
private String getValueByKey (Map<String, Object> objectMap, String value) {
String foundValue = null;
for (var entry : objectMap.entrySet()){
if(entry.getValue() instanceof String && entry.getKey().equalsIgnoreCase(value)){
foundValue = entry.getValue().toString();
break;
}
if (entry.getValue() instanceof Map) {
Map<String, Object> map = (Map<String, Object>) entry.getValue();
getValueByKey(map, value);
} else if (entry.getValue() instanceof List) {
List<?> list = (List<?>) entry.getValue();
list.forEach(listEntry -> {
if (listEntry instanceof Map) {
Map<String, Object> map = (Map<String, Object>) listEntry;
getValueByKey(map, value);
}
});
}
}
return foundValue;
}
private String getValueFromBracketString(String bracketValue) {
return bracketValue.replaceAll("\\{(.*?)\\}", "$1");
}
I am really struggling on how to get the parent object. Also I have to iterate to whole Map first to locate the value with "{}" to replace and then to search for the key.
The concrete question is, How to get the parent object? and any approach on how to improve this logic.
I think you can add an argument for parent. That is...
private void ReplaceAllVariablesMap (Map<String, Object> objectMap, Map<String, Object> parent) {
And then you have to put parent when calling it.
ReplaceAllVariablesMap(map, lastEntry);
Otherwise, you need to add a member in "Menu" for "parent" so that you can get it.
public class Menu {
Menu parent;

How to parse JSON String to String in Java

I have a JSON string, how can I parse it and just get the valueString in java?
{
"resourceType": "Bundle",
"id": "0",
"entry": [
{
"resource": {
"resourceType": "Basic",
"extension": [
{
"url": "http://ith.sahra.com/extensions#uploadid",
"valueString": "1589355494289_655"
}
]
}
}
]
}
This is my code, I tried to call extension I got null but I got response while calling entry
public static String ParsingValue(String valuepass) throws org.json.simple.parser.ParseException{
Object obj = new JSONParser().parse(valuepass);
JSONObject jo = (JSONObject) obj;
//JSONArray msg = (JSONArray) jo.getClass();
JSONArray result = (JSONArray) jo.get("entry");
}
The easiest way to parse a string through Jackson parser
Eg:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"anshul\",\"age\":29}";
Map<String, Object> map = mapper.readValue(json, Map.class);
Try this, it should solve your problem.

Parse JSON object recursively - Java

I have a JSON with list of Objects and any of the item in the list can have null or the same object as a value for a key. I am looking for a faster way to parse the json to arrive at my final result.
The data structure looks like -
[
{
"firstName": "Bruce",
"familyMembers": null
},
{
"firstName": "Gates Family",
"familyMembers": [
{
"firstName": "Bill",
"familyMembers": null
},
{
"firstName": "Steve",
"familyMembers": null
}
]
},
{
"firstName": "Lee",
"familyMembers": null
},
{
"firstName": "Chan",
"familyMembers": null
}
]
The output should be a set = ("Bruce", "Bill", "Steve", "Lee", "Chan").
I am looking for a best possible way to do this in Java, such that i dont increase my response time by getting caught in this parsing hell.
Appreciate your time on this.
Try my recursive implementation.
public static void jsonArrayToSet(JSONArray jAry, Set<String> result, String targetKey, String subArrayKey, boolean includeNode){
try {
for (int i = 0; i < jAry.length(); i++) {
JSONObject jObj = jAry.getJSONObject(i);
boolean hasSubArray = false;
JSONArray subArray = null;
if(jObj.has(subArrayKey)){
Object possibleSubArray = jObj.get(subArrayKey);
if(possibleSubArray instanceof JSONArray){
hasSubArray = true;
subArray = (JSONArray) possibleSubArray;
}
}
if(hasSubArray){
if(includeNode){
result.add(jObj.getString(targetKey));
}
jsonArrayToSet(subArray, result, targetKey, subArrayKey, includeNode);
} else {
result.add(jObj.getString(targetKey));
}
}
} catch (JSONException e){
e.printStackTrace();
}
}
jAry: The source JSONArray.
result: The Set you want to write in.
targetKey: The key that maps to an entry which you want to add to result.
subArrayKey: The key that map to a sub-JSONArray.
includeNode: When current JSONOnject is a node containing sub-array, add it to result or not.
In your case, you can call:
jsonArrayToSet(yourJsonArray, yourSet, "firstName", "familyMembers", false);
As mentioned in my comment.
Your first issue would be the content in your JSON file. Based on the standard, it should be wrapped around with a set of { }.
Example
{
"members": [
{
"firstName": "Bruce",
"familyMembers": null
},
{
"firstName": "Gates Family",
"familyMembers": [
{
"firstName": "Bill",
"familyMembers": null
},
{
"firstName": "Steve",
"familyMembers": null
}
]
},
{
"firstName": "Lee",
"familyMembers": null
},
{
"firstName": "Chan",
"familyMembers": null
}
]
}
Also, I think the value "Gates Family" should be part of the output? Since it is under the "FirstName" attribute.
Anyway, here is my solution that is based on the org.json library. It also uses Goggle's GSon library where I use it for reading the JSON file.
import org.json.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
public class solution {
public static final String JSON_DATA_FILE_PATH = "./data/source_37848106.json";
private static boolean hasMoreFamilyName(JSONObject json) {
return json.has("familyMembers") && json.get("familyMembers") != JSONObject.NULL;
}
private static void trackFirstName(Map<String, Integer> nameTracker, JSONObject json) {
if (!nameTracker.containsKey(json.getString("firstName"))) {
nameTracker.put(json.getString("firstName"), /*DUMMY VALUE =*/1);
}
}
private static void getNames(Map<String,Integer> nameTracker, JSONArray jsonArr) {
for (int i = 0; i< jsonArr.length(); i++) {
JSONObject item = jsonArr.getJSONObject(i);
if (hasMoreFamilyName(item)) {
getNames(nameTracker, item.getJSONArray("familyMembers"));
}
trackFirstName(nameTracker, item);
}
}
public static void main(String[] args) {
Map<String, Integer> nameTracker = new HashMap<>();
try {
String text = Files.toString(new File(JSON_DATA_FILE_PATH), Charsets.UTF_8);
JSONObject json = new JSONObject(text);
getNames(nameTracker, json.getJSONArray("members"));
}
catch (Exception ex) {
System.out.println("Something is wrong.");
}
for (Map.Entry<String,Integer> entry : nameTracker.entrySet()) {
System.out.println(entry.getKey());
}
}
You can use ObjectMapper defined in jackson-databind-2.6.5.jar
Define a java class with fields similar to json pattern and then just bind them like:
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
Family family=objectMapper.readValue(jsonString, Family.class);
Now you will have a java object similar to your json string pattern and you can print it the way you like.

Deserialize JSON to POJO using Retrofit and Gson

I am working with an API that responds like the following for a single user resource:
{
"data": {
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
}
}
or like the following for multiple user resources:
{
"data": [
{
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
},
],
"meta": {
"pagination": {
"total": 11,
"count": 10,
"per_page": 10,
"current_page": 1,
"total_pages": 2,
"links": {
"next": "http://api.###.com/users?page=2"
}
}
}
}
Key things to notice are:
all resources are nested under a data key, single as an object or multiple as an array of objects. This includes nested resources such as books and games in the example above.
I need to be able retrieve the values of the meta key for my pagination routines
User model
public class User extends BaseModel {
public Integer id;
public String firstName;
public String lastName;
public List<Book> books; // These will not receive the deserialized
public List<Game> games; // JSON due to the parent data key
}
Custom JSON deserializer
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
// If the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
This is all working fine but I can't figure out how to access the meta key for pagination.
Ideally I would get Gson to deserialize the response to the following POJO:
public class ApiResponse {
public Object data;
public Meta meta
}
and I could just cast the response field to the correct type in the response callback like the following:
Map<String, String> params = new HashMap<String, String>();
params.put("include", "books,games");
ApiClient.getClient().authenticatedUser(params, new ApiClientCallback<ApiResponse>() {
#Override
public void failure(RestError restError) {
Log.d("TAG", restError.message);
}
#Override
public void success(ApiResponse response, Response rawResponse) {
User user = (User) response.data; // Cast data field to User type
Log.d("TAG", user.firstName);
Log.d("TAG", "Total pages" + response.meta.pagination.total.toString()); // Still have access to meta key data
}
});
However the data field of the ApiResponse object is null.
My Java is very rusty and I have no idea if this is even possible nor do I understand how to go about it correctly, any help would be much appreciated.
I needed the same thing and have managed to get it working by adding an if statement to your custom serializer:
…
// If the meta key exists, consider the element to be root and don't unwrap it
if (!jsonObject.has("meta")) {
// If the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
…
The reason why the data field of your ApiResponse was null is because your original deserializer was processing the whole response and making you "loose" the root object's data and meta elements.
I've also parametized the ApiResponse class:
public class ApiResponse<T> {
public Meta meta;
public T data;
}
That way deserializing still works without creating many different Response classes, while casting isn't needed anymore and you can specify the type of ApiResponse's data field as needed (eg. ApiResponse<User> for single user resource, ApiResponse<List<User>> for multiple user resources, etc.).

Categories