I have a newline delimited JSON and I wish to change values of type map to strings using Java. An example is:
{"id": 1, "data": {}}
{"id": 1, "data": {"time": 43}}
{"id": 1, "data": {"class" : {"students" : [{"name" : "Jane"}]}}}
should be changed to
{"id": 1, "data": "{}"}
{"id": 1, "data": "{\"time\": 43}"}
{"id": 1, "data": "{\"class\" : {\"students\" : [{\"name\" : \"Jane\"}]}}"}
I wish to stringify all the data map values. How can I do this?
This is a problem that calls for a JSON processor, since this will provide for the most bullet-proof, simple solutions. Java doesn't come with built-in JSON processing, so this will require a third party library. One commonly used Java JSON library is Jackson.
Since this particular request involves working with arbitrary data formats, the Jackson Tree Model in the Jackson Databind library is a good fit.
String delimitedJson = "{\"id\": 1, \"data\": {}}\n"
+ "{\"id\": 1, \"data\": {\"time\": 43}}\n"
+ "{\"id\": 1, \"data\": {\"class\" : {\"students\" : [{\"name\" : \"Jane\"}]}}}";
StringBuilder output = new StringBuilder();
JsonMapper jsonMapper = JsonMapper.builder().build();
MappingIterator<ObjectNode> i =
jsonMapper.readerFor(ObjectNode.class).readValues(delimitedJson);
i.forEachRemaining((ObjectNode jsonNode) ->
output.append(convertObjectsToString(jsonMapper, jsonNode).toString())
.append("\n"));
System.out.println(output);
// ...
private ObjectNode convertObjectsToString(JsonMapper jsonMapper,
ObjectNode jsonNode) {
ObjectNode copy = jsonMapper.createObjectNode();
jsonNode.fields().forEachRemaining(e -> {
if (e.getValue() instanceof ObjectNode) {
copy.set(e.getKey(), new TextNode(e.getValue().toString()));
} else {
copy.set(e.getKey(), e.getValue());
}
});
return copy;
}
Explanation
The ObjectMapper class is the primary class in the Jackson Databind library, and JsonMapper is the JSON-format specific implementation.
After creating the instance (using JsonMapper.builder().build()), we iterate over each element of the newline-delimited list using ObjectReader.readValues(delimitedJson), which reads a number of whitespace separated JSON values from an input source. In this example, I'm assuming the root level JSON values in your list are objects, and not e.g. strings or ints.
MappingIterator<ObjectNode> i =
jsonMapper.readerFor(ObjectNode.class).readValues(delimitedJson);
i.forEachRemaining((ObjectNode jsonNode) ->
output.append(convertObjectsToString(jsonMapper, jsonNode).toString())
.append("\n"));
The custom convertObjectsToString method returns an ObjectNode, which is converted to JSON using toString().
The convertObjectsToString method starts by creating a new empty ObjectNode. For each property in the source node, if it's not a JSON object type, it is copied over directly. If it is a JSON object, it is converted to the JSON string representation of the object.
private ObjectNode convertObjectsToString(JsonMapper jsonMapper,
ObjectNode jsonNode) {
ObjectNode copy = jsonMapper.createObjectNode();
jsonNode.fields().forEachRemaining(e -> {
if (e.getValue() instanceof ObjectNode) {
copy.set(e.getKey(), new TextNode(e.getValue().toString()));
} else {
copy.set(e.getKey(), e.getValue());
}
});
return copy;
}
Related
I am having trouble parsing the response from an Adobe Campaign API endpoint into a POJO.
I am grabbing the data from the response:
String json = EntityUtils.toString(response.getEntity());
The data (heavily redacted) data looks like this:
{
"content": [
{
"PKey": "#9v59tLj9c.....",
"age": 36,
"birthDate": "1986-04-30",
"blackList": false,
...
},
{
"PKey": "#9f32tLj5c.....",
"age": 32,
"birthDate": "1999-05-11",
"blackList": false,
...
},
...
]
}
I'm instantiating a Jackson ObjectMapper and configuring it such that the root "content" node is ignored.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
I have tried many different ways of parsing the data into my Profile POJO, without success. There's always an issue related to it being wrapped in "content" node, or being a list of one, or something. For brevity, the code below is for a single POJO, but I have also tried with List<Profile> since, as mentioned, the response is always a List of one or more.
// object mapper
Profile profile = objectMapper.readValue(json), Profile.class)
// ERROR: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "content" (class com.example.adobecampaignprototype.Profile), not marked as ignorable (0 known properties: ])
// object reader
ObjectReader objectReader = objectMapper.readerFor(Profile.class).withRootName("content");
Profile profile = objectReader.readValue(json);
// ERROR: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `com.example.adobecampaignprototype.Profile` from Array value (token `JsonToken.START_ARRAY`)
// array node
ArrayNode arrayNode = (ArrayNode) objectMapper.readTree(json).get("content");
// ERROR: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "content" (class com.example.adobecampaignprototype.Profile), not marked as ignorable (0 known properties: ])
// json node
JsonNode jsonNodeRoot = objectMapper.readTree(json);
JsonNode jsonNodeNested = jsonNodeRoot.get("content");
JsonNode jsonNodeActual = jsonNodeNested.get(0) // to get profile at index 0
JsonNode jsonNodeActualValue = jsonNodeActual.get("PKey") // to read single property
I've tried the above in many combinations, but have never been able to successfully parse either a Profile or List. I have read the official docs exhaustively, been through tutorials on Baeldung and elsewhere. I feel like this should be a simple thing and there's probably something obvious that I'm overlooking, but unsure what it is. Would be grateful if someone could point me toward the EASY button.
Probably you are overthinking about it, for example if you take a semplified version of your json input file like below as a starting point:
{
"content": [
{
"PKey": "#9v59tLj9c....."
},
{
"PKey": "#9f32tLj5c....."
}
]
}
You can use part of the code you wrote, so one way to deserialize it is to convert your ArrayNode array of JsonNode into a Profile[] array:
#Data
public class Profile {
#JsonProperty("PKey")
private String pKey;
}
ArrayNode arrayNode = (ArrayNode) mapper.readTree(json).get("content");
Profile[] profiles = mapper.convertValue(arrayNode, Profile[].class);
//ok it prints the array [{"PKey":"#9v59tLj9c....."},{"PKey":"#9f32tLj5c....."}]
System.out.println(mapper.writeValueAsString(profiles));
I'm a new user of fasterxml.jackson lib. I need an help to undestand how use that lib.
I have an array generated from a string, that I need to include into a root node, but I didn't understand how do that.
My code is:
class RigaAnamnesi {
int codRiga;
String testo;
boolean domandaSiNo;
Integer sub = 0;
String spiegazione = "Spiegazione di prova";
}
ArrayList<RigaAnamnesi> vArray = fillArray();
String json = mapper.writeValueAsString(vArray);
JsonNode parsedJson = mapper.readTree(json);
ArrayNode outerArray = mapper.createArrayNode();
ObjectNode outerObject = mapper.createObjectNode();
outerObject.putPOJO("domande", parsedJson);
outerArray.add(outerObject);
File f = Paths.get("anamnesi.json").toFile();
mapper.writeValue(f, outerArray);
and my output is:
{
"domande": [
{
"codRiga": 1,
"testo": "Malattie del sangue e problemi di coagulazione_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova"
},
{
"codRiga": 2,
"testo": "Malattie endocrine (es.Diabete, Osteoporosi)___ ",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova"
},
{
"codRiga": 3,
"testo": "Malattie cardiache e vascolari_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova,"
},
...
but I need to include into a new root:
{
"anamnesi": {
"domande": [
{
"codRiga": 1,
"testo": "Malattie del sangue e problemi di coagulazione_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": ""
},
....
The Jackson ObjectNode represents a JSON object {}. To add properties, you can either call set(String, JsonNode) which adds another node (object, array, or simple value) or you can call putPOJO(String, Object). POJO stands for Plain Old Java Object and this call will interpret your List<RigaAnamnesi> class as an array of JSON objects.
To get to your desired structure, you do not need to print the array to a string first. Instead, you can create two objects and use putPOJO followed by set:
List<RigaAnamnesi> vArray = ...;
ObjectNode domande = mapper.createObjectNode();
// use putPOJO to make sure jackson converts the POJO to JsonNodes
domande.putPOJO("domande", vArray);
// this is the parent we will print
ObjectNode anamnesi = mapper.createObjectNode();
// use set, since domande already is a JsonNode
anamnesi.set("anamnesi", domande);
System.out.println(mapper.writeValueAsString(anamnesi));
I have a JSON node-like below. The structure of JsonNode will change dynamically.
Now I want to replace/update the value of a particular key.
Sample JSON One
{
"company": "xyz",
"employee": {
"name": "abc",
"address": {
"zipcode": "021566"
}
}
}
Sample JSON Two
{
"name": "abc",
"address": {
"zipcode": "021566"
}
}
I want to replace the zip code value 021566 to 566258. I know key name (zipcode), old and new zip code value but I don't know the path of zip code. I tried multiple ways using com.fasterxml.jackson.databind.JsonNode - put, replace
Is there any way to update in java?
JsonNodes are immutable, but you can find the value you want from a JsonNode, cast the original to an ObjectNode, replace the value, then cast that back to a JsonNode. It's a little odd, I know, but it worked for me.
public static void findAndReplaceJsonNode throws JsonProcessingException {
String jsonOne = "{ \"company\" : \"xyz\", \"address\" : { \"zipcode\" : \"021566\", \"state\" : \"FL\" } }";
String jsonTwo = "{ \"company\" : \"abc\", \"address\" : { \"zipcode\" : \"566258\", \"state\" : \"FL\" } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode nodeOne = mapper.readTree(jsonOne);
JsonNode nodeTwo = mapper.readTree(jsonTwo);
JsonNode findNode = nodeTwo.get("address");
ObjectNode objNode = (ObjectNode) nodeOne;
objNode.replace("address", findNode);
nodeOne = (JsonNode) objNode;
System.out.println(nodeOne);
}
Output:
{"company":"xyz","address":{"zipcode":"566258","state":"FL"}}
Disclaimer: While I do some JSON parsing and processing on a regular basis, I certainly wouldn't say that I'm adept at it or tree traversals with them. There is more than likely a better way to find the value of a child in a JsonNode than by taking the entire child as a node, replacing it's one value and then replacing the node. This should work for you in a pinch, though :)
I have the following JSON, generated in the Android application:
{
"Details": {
"ClaimDate": "08/10/2019",
"HFCode": "55555"
},
"Items": [
{
"Item": {
"ItemCode": "Y203",
"ItemPrice": "20",
"ItemQuantity": "1"
}
}
],
"Services": [
{
"Service": {
"ServiceCode": "X105",
"ServicePrice": "200",
"ServiceQuantity": "1"
}
}
]
}
On the server side, I need this structure
{
"details": {
"ClaimDate": "08/10/2019",
"HFCode": "55555"
},
"items": [
{
"itemCode": "Y200",
"itemPrice": 0,
"itemQuantity": 0
}
],
"services": [
{
"serviceCode": "X100",
"servicePrice": 0,
"serviceQuantity": 0
}
]
}
Is there a way to customize this on the Android application side?
I try to do it manually, but I can't get a satisfactory result
You can use a transformer function which will take the first json/object as input and returns the second json/object as output. Unfortunately, since your keys and data types are different, standard libraries will not able to do this. If you want to use Jackson or Gson, you will have to play with Custom (De) Serializers.
If you are using Jackson (One of the most popular JSON libraries) and you just want to transform the given JSON string into another one, then you can achieve this by following way:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonStr);
ObjectNode rootNew = mapper.createObjectNode();
rootNew.put("details", root.get("Details"));
JsonNode itemNode = root.get("Items").get(0).get("Item");
ObjectNode itemsNodeNew = mapper.createObjectNode();
itemsNodeNew.put("itemCode", itemNode.get("ItemCode"));
itemsNodeNew.put("itemPrice", itemNode.get("ItemPrice"));
itemsNodeNew.put("itemQuantity", itemNode.get("ItemQuantity"));
rootNew.put("items", mapper.createArrayNode().add(itemsNodeNew));
JsonNode serviceNode = root.get("Services").get(0).get("Service");
ObjectNode serviceNodeNew = mapper.createObjectNode();
serviceNodeNew.put("serviceCode", serviceNode.get("ServiceCode"));
serviceNodeNew.put("servicePrice", serviceNode.get("ServicePrice"));
serviceNodeNew.put("serviceQuantity", serviceNode.get("ServiceQuantity"));
rootNew.put("services", mapper.createArrayNode().add(serviceNodeNew));
System.out.println(rootNew.toString());
But if you want to convert the JSON string to POJO for further manipulation, you can directly deserialize and serialize it.
I have a different use case where fields in POJO itself stores JSON data, basically grouping of all data.
Now i want the complete JSON generated from above POJO.
At present i am using this method
private static Gson gson = new Gson();
public static String convertObjectToJSON(Object object) throws Exception {
String jsonResponse = gson.toJson(object);
return jsonResponse;
}
But getting output with escape characters around double quotes like below
{ \"_id\" : 234242, \"name\" : \"carlos\"}
I tried various options in GsonBuilder, but not working.
Basically, i am just grouping multiple JSON data and sending it across.
Could you please do the needful help to get rid of the escape characters around double quotes.
UPDATE:
Question is : I have 3 JSONs and need to combine them into a single JSON and need to pass it to html5 client through Spring MVC. As of now i am added the 3 JSONs into a POJO and trying to convert the same to JSON. Same is explained above.
Thanks & Regards
Venkat
I have tried below sample code and it doesn't have any escape characters around double quotes.
class MyPOJO{
private int _id;
private String name;
// getter & setter
}
String json="{ \"_id\" : 234242, \"name\" : \"carlos\"}";
MyPOJOobj=new Gson().fromJson(json, MyPOJO.class);
System.out.println(new Gson().toJson(obj));
output:
{"_id":234242,"name":"carlos"}
EDIT
If you want to combine 3 JSON string then It will be stored as List of object as illustrated below:
sample code:
String json = "[" +
" { \"_id\" : 1, \"name\" : \"A\"}," +
" { \"_id\" : 2, \"name\" : \"B\"}," +
" { \"_id\" : 3, \"name\" : \"C\"}" +
"]";
Type type = new TypeToken<ArrayList<MyPOJO>>() {}.getType();
ArrayList<MyPOJO> obj = new Gson().fromJson(json, type);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(obj));
output:
[
{
"_id": 1,
"name": "A"
},
{
"_id": 2,
"name": "B"
},
{
"_id": 3,
"name": "C"
}
]