I want to create a JSON like the following using Jackson in Java. I am able to do it but for a single key. In this case "TestProject1-staging" it is not able to save "children" and "vars" simultaneously.
"TestProject1-staging": {
"children" : [ "TestProject1-staginga", "TestProject1-stagingb", "TestProject1-stagingc" ],
"vars": {
"projects": {
"TestProject1": {
"app_tier": "apptier",
"remote_dir": "/release/Test1"
}
}
}
}
This is the code that I wrote:
ObjectMapper mapper = new ObjectMapper();
ObjectNode projects = mapper.createObjectNode();
ObjectNode finalObj = mapper.createObjectNode();
ObjectNode proRootNode = mapper.createObjectNode();
ObjectNode children = mapper.createObjectNode();
ObjectNode proRoot = mapper.createObjectNode();
proRoot.put("app_tier", inventoryContainer.appTier);
proRoot.put("remote_dir", inventoryContainer.remoteDirectory);
proRootNode.set(projectRoot, proRoot);
projects.set("projects", proRootNode);
children.put("children",
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(inventoryContainer.groups));
stagingVarNotSet = false;
finalObj.set(inventoryContainer.projectName, children);
varNode.set("vars", projects);
//finalObj.set(inventoryContainer.projectName, varNode);
System.out.println(StringEscapeUtils.unescapeJava(
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(finalObj)));
As you can see, the commented line tries to set the vars variable. If I uncomment it, vars will be printed but children will be lost. In the current format, it prints the children but not the vars.
So how can I print both of them together?
Check if this works for you.
JSonCreator .java
package json;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class JSonCreator {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.createObjectNode();
// Create TestProject1 JSON Object
ObjectNode testProject1Node = mapper.createObjectNode();
testProject1Node.put("app_tier", "apptier");
testProject1Node.put("remote_dir", "/release/Test1");
// Create projects JSON Object
ObjectNode projectsNode = mapper.createObjectNode();
projectsNode.set("TestProject1", testProject1Node);
// Create vars JSON Object
ObjectNode varsNode = mapper.createObjectNode();
varsNode.set("projects", projectsNode);
// Create children JSON Array
ArrayNode childrenArrayNode = mapper.createArrayNode();
childrenArrayNode.add("TestProject1-staginga");
childrenArrayNode.add("TestProject1-stagingb");
childrenArrayNode.add("TestProject1-stagingc");
// Create children JSON object
ObjectNode childrenNode = mapper.createObjectNode();
childrenNode.set("children", childrenArrayNode);
// Create TestProject1-staging" JSON object
ObjectNode testProject1stagingNode = mapper.createObjectNode();
testProject1stagingNode.set("children", childrenArrayNode);
testProject1stagingNode.set("vars", varsNode);
// append into root node
((ObjectNode) rootNode).set("TestProject1-staging",
testProject1stagingNode);
// convert ObjectNode to pretty-print JSON
String json = null;
try {
json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(
rootNode);
// print json
System.out.println(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
Output:
{
"TestProject1-staging" : {
"children" : [ "TestProject1-staginga", "TestProject1-stagingb", "TestProject1-stagingc" ],
"vars" : {
"projects" : {
"TestProject1" : {
"app_tier" : "apptier",
"remote_dir" : "/release/Test1"
}
}
}
}
}
I am trying to convert a json file to a list of strings, but it is throwing expected -> error
I've already tried TypeReference<List<String>> List<String> but it throws a different error each time. Look at line 28 for the error location.
Error Message:
[error] /home/willroy/Code/playframework/database/app/controllers/HomeController.java:28:1: -> expected
[error] return mapper.readValue(new File("/tmp/notes.json"), List(){});
[error] (Compile / compileIncremental) javac returned non-zero exit code
[error] Total time: 0 s, completed 12-Jun-2019 11:05:01
public class HomeController extends Controller {
public Result get() {
return ok("")
WS.url("localhost:9000")
.seteContentType("/")
.post(getJson());
}
public Result post(String text) {
List<String> noteJson = getJson();
noteJson.append(text);
return ok("");
}
private List<String> getJson() {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(new File("/tmp/notes.json"), List<String>(){});
}
private void saveJson(List<String> noteJson) {
FileWriter file = new FileWriter("/tmp/notes.json");
file.write(notJson.toJSONString());
}
}
SOLVED:
Did not import enough at the top of the file (File, List, etc...) XD.
Just change your code by using this example below :
private List<String> getJson() {
List<String> texts = new ArrayList<String>();
ObjectMapper mapper = new ObjectMapper();
final JsonNode nodes = objectMapper.readTree(new File("/tmp/notes.json")).get("list");
if (nodes.isArray()) {
for (final JsonNode node : nodes) {
texts.add(node.get("property").asText());
}
}
return texts;
}
You must specify the Class in the second argument,
return mapper.readValue(new File("/tmp/notes.json"), new TypeReference<List<String>>(){});
I have a Json file :
[
{
"name":"Move",
"$$hashKey":"object:79",
"time":11.32818,
"endTime":18.615535
},
{
"name":"First Red Flash",
"$$hashKey":"object:77",
"time":15.749153
},
{
"name":"Pills",
"subEventTypes":[
"pull down bottle",
"unscrew lid",
"dump pills out",
"screw lid on",
"put bottle away"
],
"$$hashKey":"object:82",
"time":25.130175,
"subEventSplits":[
26.092057,
27.425881,
31.841594,
34.268093
],
"endTime":36.234827
}
]
I tried to parse this Json file using the Jackson.
I wrote the following code:
public class Holder{
public Holder(){};
//getter and setters
String name;
List<String> subEventTypes = new ArrayList<>();
Double time;
String $$hashKey;
Double endTime;
List<Double> subEventSplits = new ArrayList<>();
}
class MapperClass{
List<Holder> list = new ArrayList<>();
}
public static void main(String[] args) throws JsonProcessingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
List<Holder> list = mapper.readValue(new File("data.json"), mapper.getTypeFactory().constructCollectionType(
List.class, Holder.class));
}
When I run the program, it showed this error : "
No suitable constructor found for type [simple type, class parseJason$Holder]: can not instantiate from JSON object (need to add/enable type information?)
".
Is there anything wrong with my code? or I have to use another way to parse my Json file.
try
list = mapper.readValue(
jsonString,
objectMapper.getTypeFactory().constructCollectionType(
List.class, Holder.class));
I'm using Jackson mapper version 2.6.5 with Spring Boot but I can seem to get SerializationFeature.INDENT_OUTPUT to work. I'm following the tutorial here. My code is as follows.
public class SerializationExampleTreeModel {
public static void main(String[] args) throws IOException {
// Create the node factory that gives us nodes.
JsonNodeFactory nodeFactory = new JsonNodeFactory(false);
StringWriter stringWriter = new StringWriter();
// create a json factory to write the treenode as json. for the example
// we just write to console
JsonFactory jsonFactory = new JsonFactory();
JsonGenerator generator = jsonFactory.createGenerator(stringWriter);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// the root node - album
JsonNode album = nodeFactory.objectNode();
album.put("Album-Title", "Kind Of Blue")
ArrayNode songs = nodeFactory.arrayNode()
songs.add("Song8").add("Song2")
album.put("Songs", songs)
ObjectNode artist = nodeFactory.objectNode()
artist.put("Name", "Alex" )
album.put( "artist", artist)
mapper.writeTree(generator, album)
println stringWriter.toString()
}
}
I always get the result:
{"Album-Title":"Kind Of Blue","Songs":["Song8","Song2"],"artist":{"Name":"Alex"}} whether I include the line mapper.configure(SerializationFeature.INDENT_OUTPUT, true) or not. What is going on?
Note: I'm compiling my code using groovyc and semi-colons aren't required.
The problem is you are using StringWriter to write the output and it ignores the formatting you set on ObjectMapper as expected. Instead, use:
System.out.println(mapper.writeValueAsString(album));
If you prefer to use as you are using, you can declare the printer like this before writing the tree:
generator.setPrettyPrinter(new DefaultPrettyPrinter());
mapper.writeTree(generator, album);
This will allow the correct output with:
stringWriter.toString()
I am using the Jackson (1.9.x) library to parse JSON into a Map:
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = (Map<String,Object>) mapper.readValue(jsonStr, Map.class);
Is there a way to tell the Jackson parser to lowercase all the names of the keys? I tried using a Jackson PropertyNamingStrategy, but that didn't work - it only seems to be useful when it is getting mapped onto some bean, not a Map.
Clarifications:
I do not want to have to precreate beans for the JSON - I only want dynamic Maps
The JSON keys coming in will not be lowercase, but I want all the map keys to be lowercase (see example below)
The JSON is rather large and heavily nested, so regular expression replacements of the incoming JSON or creating a new map manually after the Jackson parsing is not at all desired.
Incoming JSON:
{"CustName":"Jimmy Smith","Result":"foo","CustNo":"1234"}
The Java map would have:
"custname" => "Jimmy Smith"
"result" => "foo"
"custno" => "1234"
[UPDATE]: The answer I gave below doesn't fully solve the problem. Still looking for a solution.
(nb this solution is tested only with Jackson 2)
It's possible to do this by wrapping the JsonParser and simply applying .toLowerCase() to all field names:
private static final class DowncasingParser extends JsonParserDelegate {
private DowncasingParser(JsonParser d) {
super(d);
}
#Override
public String getCurrentName() throws IOException, JsonParseException {
if (hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
return delegate.getCurrentName().toLowerCase();
}
return delegate.getCurrentName();
}
#Override
public String getText() throws IOException, JsonParseException {
if (hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
return delegate.getText().toLowerCase();
}
return delegate.getText();
}
}
You then have to have a custom JsonFactory to apply your wrapper, as in this test:
#Test
public void downcase_map_keys_by_extending_stream_parser() throws Exception {
#SuppressWarnings("serial")
ObjectMapper mapper = new ObjectMapper(new JsonFactory() {
#Override
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(data, offset, len, ctxt));
}
#Override
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(in, ctxt));
}
#Override
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(r, ctxt));
}
#Override
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
throws IOException {
return new DowncasingParser(super._createParser(data, offset, len, ctxt, recyclable));
}
});
assertThat(
mapper.reader(Map.class)
.with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
.with(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
.readValue("{CustName:'Jimmy Smith', CustNo:'1234', Details:{PhoneNumber:'555-5555',Result:'foo'} } }"),
equalTo((Map<String, ?>) ImmutableMap.of(
"custname", "Jimmy Smith",
"custno", "1234",
"details", ImmutableMap.of(
"phonenumber", "555-5555",
"result", "foo"
)
)));
}
I figured out one way to do it. Use a org.codehaus.jackson.map.KeyDeserializer, put it in a SimpleModule and register that module with the Jackson ObjectMapper.
import org.codehaus.jackson.map.KeyDeserializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.Version;
// ...
class LowerCaseKeyDeserializer extends KeyDeserializer {
#Override
public Object deserializeKey(String key, DeserializationContext ctx)
throws IOException, JsonProcessingException {
return key.toLowerCase();
}
}
// ...
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("LowerCaseKeyDeserializer",
new Version(1,0,0,null));
module.addKeyDeserializer(Object.class, new LowerCaseKeyDeserializer());
mapper.registerModule(module);
Map<String,Object> map =
(Map<String,Object>) mapper.readValue(jsonStr, Map.class);
[UPDATE]: Actually this only will lowercase the top level map keys, but not nested keys.
If the input is:
{"CustName":"Jimmy Smith","CustNo":"1234","Details":{"PhoneNumber": "555-5555", "Result": "foo"}}
The output in the map, unfortunately, will be:
{"custname"="Jimmy Smith", "custno"="1234", "details"={"PhoneNumber"="555-5555", "Result"="foo"}}
With Jackson there isn't any function that will lower the keys in a nested fashion. Atleast not that I know of. I wrote this simple recursive code that does the job.
public JSONObject recursiveJsonKeyConverterToLower(JSONObject jsonObject) throws JSONException
{
JSONObject resultJsonObject = new JSONObject();
#SuppressWarnings("unchecked") Iterator<String> keys = jsonObject.keys();
while(keys.hasNext())
{
String key = keys.next();
Object value = null;
try
{
JSONObject nestedJsonObject = jsonObject.getJSONObject(key);
value = this.recursiveJsonKeyConverterToLower(nestedJsonObject);
}
catch(JSONException jsonException)
{
value = jsonObject.get(key);
}
resultJsonObject.put(key.toLowerCase(), value);
}
return resultJsonObject;
}
Passed String:
String json = "{'Music': 0, 'Books': {'Biology': 1.1, 'Chemistry': {'Inorganic': true, 'Organic': ['Atom', 'Molecule']}}, 'Food': {'Chicken': [1, 2, 3]}}";
Output:
{"music":0,"books":{"biology":1.1,"chemistry":{"inorganic":true,"organic":["Atom","Molecule"]}},"food":{"chicken":[1,2,3]}}
Its also easy to get Map<String, Object> instead of JSONObject (which is what you want) by making resultJsonObject to be of type Map and other little tweaks.
WARNING: for nested JSON, the result would be of type Map<String, Map<String, Object>> depending on how nested is your json object.
public void setKeyName(String systemName){
this.systemName = systemName.toLowerCase();
}
Below is the second JSON message:
{
"ModeL":"Tesla",
"YeaR":"2015"
}
Normally, default ObjectMapper cannot deserialize this message into a CarInfo object. With following configuration, it’s possible:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
CarInfo info = objectMapper.readValue(data, CarInfo.class); //'data' contains JSON string
This deserialization is valid. his deserialization is valid.
https://mtyurt.net/post/jackson-case-insensitive-deserialization.html