jackson xml lists deserialization recognized as duplicate keys - java

I'm trying to convert xml into json using jackson-2.5.1 and jackson-dataformat-xml-2.5.1
The xml structure is received from web server and unknown, therefore I can't have java class to represent the object, and I'm trying to convert directly into TreeNode using ObjectMapper.readTree.
My problem is jackson failing to parse lists. It is takes only the last item of the list.
code:
String xml = "<root><name>john</name><list><item>val1</item>val2<item>val3</item></list></root>";
XmlMapper xmlMapper = new XmlMapper();
JsonNode jsonResult = xmlMapper.readTree(xml);
The json result:
{"name":"john","list":{"item":"val3"}}
If I enable failure on duplicate keys xmlMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY), exception is thrown:
com.fasterxml.jackson.databind.JsonMappingException: Duplicate field 'item' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled
Is there any feature which fixes this problem? Is there a way for me to write custom deserializer which in event of duplicate keys turn them into array?

I use this approach:
Plugin a serializer into XmlMapper using a guava multimap. This puts everything into lists.
Write out the json using SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED. This unwrapps all lists with size==1.
Here is my code:
#Test
public void xmlToJson() {
String xml = "<root><name>john</name><list><item>val1</item>val2<item>val3</item></list></root>";
Map<String, Object> jsonResult = readXmlToMap(xml);
String jsonString = toString(jsonResult);
System.out.println(jsonString);
}
private Map<String, Object> readXmlToMap(String xml) {
try {
ObjectMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().addDeserializer(Object.class, new UntypedObjectDeserializer() {
#SuppressWarnings({ "unchecked", "rawtypes" })
#Override
protected Map<String, Object> mapObject(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonToken t = jp.getCurrentToken();
Multimap<String, Object> result = ArrayListMultimap.create();
if (t == JsonToken.START_OBJECT) {
t = jp.nextToken();
}
if (t == JsonToken.END_OBJECT) {
return (Map) result.asMap();
}
do {
String fieldName = jp.getCurrentName();
jp.nextToken();
result.put(fieldName, deserialize(jp, ctxt));
} while (jp.nextToken() != JsonToken.END_OBJECT);
return (Map) result.asMap();
}
}));
return (Map) xmlMapper.readValue(xml, Object.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static public String toString(Object obj) {
try {
ObjectMapper jsonMapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true);
StringWriter w = new StringWriter();
jsonMapper.writeValue(w, obj);
return w.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
It prints
{
"list" : {
"item" : [ "val1", "val3" ]
},
"name" : "john"
}
Altogether it is a variante of this approach, which comes out without guava multimap:
https://github.com/DinoChiesa/deserialize-xml-arrays-jackson
Same approach is used here:
Jackson: XML to Map with List deserialization

I ran into the same problem and decided to roll my own using straightforward DOM. The main problem is that XML does not really lends itself to a Map-List-Object type mapping like JSon does. However, with some assumptions, it is still possible:
Text are stored in null keys either as a single String or a List.
Empty elements, i.e. are modeled with an empty map.
Here's the class in the hope that it might just help someone else:
public class DeXML {
public DeXML() {}
public Map<String, Object> toMap(InputStream is) {
return toMap(new InputSource(is));
}
public Map<String, Object> toMap(String xml) {
return toMap(new InputSource(new StringReader(xml)));
}
private Map<String, Object> toMap(InputSource input) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(input);
document.getDocumentElement().normalize();
Element root = document.getDocumentElement();
return visitChildNode(root);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new RuntimeException(e);
}
}
// Check if node type is TEXT or CDATA and contains actual text (i.e. ignore
// white space).
private boolean isText(Node node) {
return ((node.getNodeType() == Element.TEXT_NODE || node.getNodeType() == Element.CDATA_SECTION_NODE)
&& node.getNodeValue() != null && !node.getNodeValue().trim().isEmpty());
}
private Map<String, Object> visitChildNode(Node node) {
Map<String, Object> map = new HashMap<>();
// Add the plain attributes to the map - fortunately, no duplicate attributes are allowed.
if (node.hasAttributes()) {
NamedNodeMap nodeMap = node.getAttributes();
for (int j = 0; j < nodeMap.getLength(); j++) {
Node attribute = nodeMap.item(j);
map.put(attribute.getNodeName(), attribute.getNodeValue());
}
}
NodeList nodeList = node.getChildNodes();
// Any text children to add to the map?
List<Object> list = new ArrayList<>();
for (int i = 0; i < node.getChildNodes().getLength(); i++) {
Node child = node.getChildNodes().item(i);
if (isText(child)) {
list.add(child.getNodeValue());
}
}
if (!list.isEmpty()) {
if (list.size() > 1) {
map.put(null, list);
} else {
map.put(null, list.get(0));
}
}
// Process the element children.
for (int i = 0; i < node.getChildNodes().getLength(); i++) {
// Ignore anything but element nodes.
Node child = nodeList.item(i);
if (child.getNodeType() != Element.ELEMENT_NODE) {
continue;
}
// Get the subtree.
Map<String, Object> childsMap = visitChildNode(child);
// Now, check if this is key already exists in the map. If it does
// and is not a List yet (if it is already a List, simply add the
// new structure to it), create a new List, add it to the map and
// put both elements in it.
if (map.containsKey(child.getNodeName())) {
Object value = map.get(child.getNodeName());
List<Object> multiple = null;
if (value instanceof List) {
multiple = (List<Object>)value;
} else {
map.put(child.getNodeName(), multiple = new ArrayList<>());
multiple.add(value);
}
multiple.add(childsMap);
} else {
map.put(child.getNodeName(), childsMap);
}
}
return map;
}
}

You can catch that exception and do something like :
List<MyClass> myObjects = mapper.readValue(input, new TypeReference<List<MyClass>>(){});
(got it from here How to use Jackson to deserialise an array of objects)
It's a hackish approach and you will have to figure out how to resume from there.

Underscore-java library supports this XML.
String xml = "<root><name>john</name><list><item>val1</item>val2<item>val3</item></list></root>";
String json = U.xmlToJson(xml);
System.out.println(json);
Output JSON:
{
"root": {
"name": "john",
"list": {
"item": [
"val1",
{
"#item": {
"#text": "val2"
}
},
"val3"
]
}
},
"#omit-xml-declaration": "yes"
}

Related

How to create a LIST of DOM elements from Map which contains nested/complex objects

I have a Map<String, Object> field which can contain complex types. The value (Object) can contain Map, String or ArrayList my goal is to write a method that can recursively loop over the Map and create a nested DOM elements and write into List. I was able to complete it halfway through it and after that, I am unable to understand how to proceed in the recursive approach.
Basically, I want my Marshalling method to handle any complex/nested values such as Map and String and create a DOM Element recursively and store it in List.
My input Map<String, Object> can be anything like (can be more nested/complex or simple):
{google:first={google:second={google:third=Value1, google:forth={google:fifth=Value2}}}}
These values are provided as Map<String, Object> to my method. I want to identify what kind of values are incoming based on which I need to create the List<Object>. These Objects are DOM elements that I am using later to create my XML file. I am stuck at some point and unable to get desired output.
Following is the code I have so far where I am using the recursive method to build the Element. I want the Marshalling method to build the Complex element based on Map, String, and List so it can handle any type of complex UserExtensions.
public class Main {
public static void main(String[] args) throws JsonProcessingException, ParserConfigurationException {
Map<String, Object> userExtensions = new HashMap<>();
Map<String, Object> complex1 = new HashMap<>();
Map<String, Object> complex2 = new HashMap<>();
Map<String, Object> complex3 = new HashMap<>();
complex3.put("google:fifth", "Value2");
complex2.put("google:fourth", complex3);
complex2.put("google:third", "Value1");
complex1.put("google;second", complex2);
userExtensions.put("google:first", complex1);
List<Object> result = Marshalling(userExtensions);
}
private static List<Object> Marshalling(Map<String, Object> userExtensions) throws ParserConfigurationException {
List<Object> tempElement = new ArrayList<>();
javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
org.w3c.dom.Document doc = db.newDocument();
for (Map.Entry<String, Object> property : userExtensions.entrySet()) {
if (property.getValue() instanceof Map) {
Element complexElement = doc.createElement(property.getKey());
System.out.println(" Element : " + complexElement);
Marshalling((Map<String, Object>) property.getValue()).forEach(innerChildren -> {
if (innerChildren instanceof Element) {
if (((Element) innerChildren).getTextContent() != null) {
org.w3c.dom.Element newNode = doc.createElement(((Element) innerChildren).getNodeName());
newNode.setTextContent(((Element) innerChildren).getTextContent());
complexElement.appendChild(newNode);
}
}
});
tempElement.add(complexElement);
} else {
Element simpleElement = doc.createElement(property.getKey());
simpleElement.setTextContent(((String) property.getValue()));
System.out.println(tempElement.size());
tempElement.add(simpleElement);
}
}
return tempElement;
}
}
I was referring to the following answer a bit: https://stackoverflow.com/a/25359634/7584240
I hope I have provided all the needed explanation. Any help or suggestion would be really helpful for me. Thanks in advance.
I tried a lot of things and did some research, I was able to get it, posting the answer here as it can be useful to someone in the future:
public List<Object> Marshalling(Map<String, Object> userExtensions) throws ParserConfigurationException {
if (userExtensions == null) {
return null;
}
List<Object> tempElement = new ArrayList<>();
for (Map.Entry<String, Object> property : userExtensions.entrySet()) {
Element root = document.createElement(property.getKey());
if (property.getValue() instanceof Map) {
List<Object> mapElements = Marshalling((Map<String, Object>) property.getValue());
mapElements.forEach(innerChildren -> {
if (innerChildren instanceof Element) {
if (((Element) innerChildren).getTextContent() != null) {
root.appendChild(document.appendChild((Element) innerChildren));
}
}
});
tempElement.add(root);
} else if (property.getValue() instanceof String) {
root.setTextContent(((String) property.getValue()));
tempElement.add(root);
} else if (property.getValue() instanceof ArrayList) {
for (Object dupItems : (ArrayList<Object>) property.getValue()) {
if (dupItems instanceof Map) {
Element arrayMap = document.createElement(property.getKey());
List<Object> arrayMapElements = Marshalling((Map<String, Object>) dupItems);
arrayMapElements.forEach(mapChildren -> {
if (mapChildren instanceof Element) {
if (((Element) mapChildren).getTextContent() != null) {
arrayMap.appendChild(document.appendChild((Element) mapChildren));
}
}
});
tempElement.add(arrayMap);
} else if (dupItems instanceof String) {
Element arrayString = document.createElement(property.getKey());
arrayString.setTextContent((String) dupItems);
tempElement.add(arrayString);
}
}
}
}
return tempElement;
}

Restructuring JSON file in JAVA

I am having the following sample from a JSON file:
[
{
"0":
{
"File":"file1.java",
"Class":"com.ETransitionActionType",
"Method":"values",
"Annotation":"Not Found"
}
},
{
"1":
{
"File":"file2.java",
"Class":"com.ETransitionParams",
"Method":"values",
"Annotation":"Not Found"
}
},
{
"2":
{
"File":"file3.java",
"Class":"com.phloc.commons.id.IHasID",
"Method":"getID",
"Annotation":"Not Found"
}
},
{
"4":
{
"File":"file3.java",
"Class":"com.ExecuteTransitionActionHandler",
"Method":"createBadRequestResponse",
"Annotation":"Not Found"
}
},
{
"5":
{
"File":"file3.java",
"Class":"com.ExecuteTransitionActionHandler",
"Method":"extractParametersFromAction",
"Annotation":"Not Found"
}
}]
How can I restructure this file using java so that it looks like:
[{
"file1.java": {
"com.ETransitionActionType": {
"values": {
"Annotation": "Not Found"
}
}
}
},
{
"file2.java": {
"com.ETransitionParams": {
"values": {
"Annotation": "Not Found"
}
}
}
},
{
"file3.java": {
"com.phloc.commons.id.IHasID": {
"getID": {
"Annotation": "Not Found"
}
},
"com.ExecuteTransitionActionHandler": {
"getID": {
"Annotation": "Not Found"
},
"extractParametersFromAction": {
"Annotation": "Not Found"
}
}
}
}
]
i.e. Going through the JSON file, searching it, and wherever the "File" attribute has the same value("file3.java" for example), we list all the relevant classes and methods inside and the same applies for the "Class" attribute, if it has the same name, we list all the methods inside it(So it's like comparing and sorting the values for the "File" and "Class" attributes).
I started with JSON simple library and wrote like the code below, but don't know how to go further!
Object object = (JSONArray)parser.parse(new FileReader("rawOutput.json"));
JSONArray jsonArray = (JSONArray) object;
for(int i = 0; i < jsonArray.size(); i++) {
System.out.println(jsonArray.get(i));
JSONObject jsonObject = (JSONObject)jsonArray.get(i);
String c = jsonObject.get("" + i + "").toString();
}
Any ideas? Your help is really appreciated!!!
I wrote a code to do what do you need but first you have to add this library to your project if you don't have already org.json.zip library, because I didn't have a library for parsing Json texts so I used this library for formatting the Json data, and I'm sorry if you don't understand the code completely because your request isn't so easy as yourself know and I created three functions to get the result and although I wrote some comments to understand easily, this is the code:-
Edit
...
import org.json.*;
...
...
public static void main(String[] args) throws JSONException {
System.out.println(getFormattedJson("json text"));
}
private static String getFormattedJson(String text) throws JSONException{
JSONArray result = new JSONArray();
JSONArray jsonArray = null;
//get the json array
jsonArray = new JSONArray(text);
//loop through items in the array and insert them formatted to the result
for (int i = 0; i < jsonArray.length(); i++) {
//get object inside the number
JSONObject object = getJsonChild(jsonArray.getJSONObject(i));
//get these attributes
String file = object.getString("File");
String clas = object.getString("Class");
String meth = object.getString("Method");
String anno = object.getString("Annotation");
//create a custom type of the object's attributes
Map<String, String> map = new HashMap<>();
map.put("Annotation", anno);
Map<String, Object> map1 = new HashMap<>();
map1.put(meth, map);
Map<String, Object> map2 = new HashMap<>();
map2.put(clas, map1);
Map<String, Object> map3 = new HashMap<>();
map3.put(file, map2);
//loop through repeating values to also add them to one value as you expected
for (int j = jsonArray.length() - 1; j > i; j--) {
JSONObject obj = getJsonChild(jsonArray.getJSONObject(j));
String file1 = obj.getString("File");
String clas1 = obj.getString("Class");
String meth1 = obj.getString("Method");
String anno1 = obj.getString("Annotation");
if (file1.equals(file)) {
if (map2.containsKey(clas1)) {
if (childrenContains(map2, meth1)) {
//if the difference was annotation value
map.put("Annotation", anno1);
} else {
//if the difference was method names
Map<String, String> map_ = new HashMap<>();
map_.put("Annotation", anno1);
((Map<String, Object>) map2.get(clas1)).put(meth1, map_);
}
} else {
//if the difference was class names
Map<String, String> map_ = new HashMap<>();
map_.put("Annotation", anno1);
Map<String, Object> map1_ = new HashMap<>();
map1_.put(meth1, map_);
map2.put(clas1, map1_);
}
//remove the (value added) object
jsonArray.remove(j);
}
}
//add the map to the result
result.put(map3);
}
return result.toString(4);
}
private static boolean childrenContains(Map<String, Object> map1, String meth1) {
for (String childKey : map1.keySet()) {
Map<String, Object> child = (Map<String, Object>) map1.get(childKey);
if (child.containsKey(meth1))
return true;
}
return false;
}
private static JSONObject getJsonChild(JSONObject object) throws JSONException {
Iterator<String> keys = object.keys();
String key = "";
while (keys.hasNext()) {
key = (String) keys.next();
}
return object.getJSONObject(key);
}
And the result for your sample using my code is:-
[
{"file1.java": {"com.ETransitionActionType": {"values": {"Annotation": "Not Found"}}}},
{"file2.java": {"com.ETransitionParams": {"values": {"Annotation": "Not Found"}}}},
{"file3.java": {
"com.ExecuteTransitionActionHandler": {
"createBadRequestResponse": {"Annotation": "Not Found"},
"extractParametersFromAction": {"Annotation": "Not Found"}
},
"com.phloc.commons.id.IHasID": {"getID": {"Annotation": "Not Found"}}
}}
]
And if you want to get the json data from a file so use the following function to create the JSONArray easily:-
private static JSONArray readFromFile(String filePath){
try {
BufferedReader br = new BufferedReader(new FileReader(filePath));
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
return new JSONArray(sb.toString());
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
And use it instead the text json data:-
...
//get the json array
jsonArray = readFromFile("FilePath");
...
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Foo {
public static void main(String... args) throws IOException {
String json = formatJson(new FileReader("rawOutput.json"));
System.out.println(json);
}
public static String formatJson(Reader reader) throws IOException {
// group array items by fileName
final Function<List<Map<String, Object>>, Map<String, List<Object>>> groupByFileName =
data -> data.stream().collect(Collectors.groupingBy(map -> (String)map.get("File"), TreeMap::new,
Collectors.mapping(Function.identity(), Collectors.toList())));
// convert source item structure into required
final Function<Map.Entry<String, List<Object>>, Map<String, Object>> convert = entry -> {
Map<String, Map<String, Map<String, String>>> tmp = new LinkedHashMap<>();
entry.getValue().stream()
.map(value -> (Map<String, String>)value)
.forEach(map -> {
Map<String, Map<String, String>> classes = tmp.computeIfAbsent(map.get("Class"), cls -> new TreeMap<>());
Map<String, String> methods = classes.computeIfAbsent(map.get("Method"), method -> new TreeMap<>());
map.entrySet().stream()
.filter(e -> !"Class".equals(e.getKey()) && !"Method".equals(e.getKey()) && !"File".equals(e.getKey()))
.forEach(e -> methods.put(e.getKey(), e.getValue()));
});
return Collections.singletonMap(entry.getKey(), tmp);
};
ObjectMapper mapper = new ObjectMapper();
// read json as array of Maps
List<Map<String, Object>> data = Arrays.stream(mapper.readValue(reader, Map[].class))
.map(map -> map.values().iterator().next())
.map(item -> (Map<String, Object>)item)
.collect(Collectors.toList());
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(groupByFileName.apply(data).entrySet().stream()
.map(convert).collect(Collectors.toList()));
}
}
You could create a map of maps to represent your grouping by "File" and "Class" for your list of (inner) JSON objects. It might look similar to
final Function<JSONObject, String> fileFunction = (JSONObject jsonObject) -> jsonObject.getString("File");
final Function<JSONObject, String> classFunction = (JSONObject jsonObject) -> jsonObject.getString("Class");
final Map<String, Map<String, List<JSONObject>>> groupedJsonObjects = jsonObjects.stream()
.collect(Collectors.groupingBy(fileFunction, Collectors.groupingBy(classFunction)));

Jackson deserialize Double

I have a problem with deserialization of the JSON using Jackson in my rest-assured tests.
In my JSON I have a key "value" that can be an array of Strings or object like Boolean.
{
"value": ["value1", "value2"]
}
or
{
"value": 2272204.2426
}
So I wrote custom deserializer for this field:
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
if (node.isArray()) {
List<String> list = new ArrayList<>();
for (JsonNode elementNode : node) {
list.add(oc.treeToValue(elementNode, String.class));
}
return list;
} else {
if(node.isDouble()) {
return oc.treeToValue(node, Double.class);
}
else if(node.isBoolean()){
return oc.treeToValue(node, Boolean.class);
}
else {
return oc.treeToValue(node, String.class);
}
}
}
In the end I've noticed that numeric value like 2272204.2426 is deserialized to 2272204.2
I tried to desierialize it using Gson and it works well. Do you have any idea why using Jackson there is lack of decimal part?
I've tried to debug the code and I've noticed that on this step JsonNode node = oc.readTree(jp); the value is 2272204.2
Why not use ObjectMapper from Jackson? You can add DeserializationFeature to it unlike ObjectCodec. Mapper is actually extending Codec, but with more features that you need in this case.
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
JsonNode node = //node where the value is defined as Double
Double value = null;
try {
value = mapper.treeToValue(node, Double.class);
}
catch (IOException e) {
e.printStackTrace();
}
System.out.println(value);
Use the above logic in your node.isDouble() case

Parsing JSON in Java without knowing JSON format

I am trying to parse JSON strings in Java and find the key-value pairs so that I can determine the approximate structure of the JSON object since object structure of JSON string is unknown.
For example, one execution may have a JSON string like this:
{"id" : 12345, "days" : [ "Monday", "Wednesday" ], "person" : { "firstName" : "David", "lastName" : "Menoyo" } }
And another like this:
{"url" : "http://someurl.com", "method" : "POST", "isauth" : false }
How would I cycle through the various JSON elements and determine the keys and their values? I looked at jackson-core's JsonParser. I see how I can grab the next "token" and determine what type of token it is (i.e., field name, value, array start, etc), but, I don't know how to grab the actual token's value.
For example:
public void parse(String json) {
try {
JsonFactory f = new JsonFactory();
JsonParser parser = f.createParser(json);
JsonToken token = parser.nextToken();
while (token != null) {
if (token.equals(JsonToken.START_ARRAY)) {
logger.debug("Start Array : " + token.toString());
} else if (token.equals(JsonToken.END_ARRAY)) {
logger.debug("End Array : " + token.toString());
} else if (token.equals(JsonToken.START_OBJECT)) {
logger.debug("Start Object : " + token.toString());
} else if (token.equals(JsonToken.END_OBJECT)) {
logger.debug("End Object : " + token.toString());
} else if (token.equals(JsonToken.FIELD_NAME)) {
logger.debug("Field Name : " + token.toString());
} else if (token.equals(JsonToken.VALUE_FALSE)) {
logger.debug("Value False : " + token.toString());
} else if (token.equals(JsonToken.VALUE_NULL)) {
logger.debug("Value Null : " + token.toString());
} else if (token.equals(JsonToken.VALUE_NUMBER_FLOAT)) {
logger.debug("Value Number Float : " + token.toString());
} else if (token.equals(JsonToken.VALUE_NUMBER_INT)) {
logger.debug("Value Number Int : " + token.toString());
} else if (token.equals(JsonToken.VALUE_STRING)) {
logger.debug("Value String : " + token.toString());
} else if (token.equals(JsonToken.VALUE_TRUE)) {
logger.debug("Value True : " + token.toString());
} else {
logger.debug("Something else : " + token.toString());
}
token = parser.nextToken();
}
} catch (Exception e) {
logger.error("", e);
}
}
Is there a class in jackson or some other library (gson or simple-json) that produces a tree, or allows one to cycle through the json elements and obtain the actual key names in addition to the values?
Take a look at Jacksons built-in tree model feature.
And your code will be:
public void parse(String json) {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
JsonNode rootNode = mapper.readTree(json);
Iterator<Map.Entry<String,JsonNode>> fieldsIterator = rootNode.fields();
while (fieldsIterator.hasNext()) {
Map.Entry<String,JsonNode> field = fieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
}
If a different library is fine for you, you could try org.json:
JSONObject object = new JSONObject(myJSONString);
String[] keys = JSONObject.getNames(object);
for (String key : keys)
{
Object value = object.get(key);
// Determine type of value and do something with it...
}
Find the following code for Unknown Json Object parsing using Gson library.
public class JsonParsing {
static JsonParser parser = new JsonParser();
public static HashMap<String, Object> createHashMapFromJsonString(String json) {
JsonObject object = (JsonObject) parser.parse(json);
Set<Map.Entry<String, JsonElement>> set = object.entrySet();
Iterator<Map.Entry<String, JsonElement>> iterator = set.iterator();
HashMap<String, Object> map = new HashMap<String, Object>();
while (iterator.hasNext()) {
Map.Entry<String, JsonElement> entry = iterator.next();
String key = entry.getKey();
JsonElement value = entry.getValue();
if (null != value) {
if (!value.isJsonPrimitive()) {
if (value.isJsonObject()) {
map.put(key, createHashMapFromJsonString(value.toString()));
} else if (value.isJsonArray() && value.toString().contains(":")) {
List<HashMap<String, Object>> list = new ArrayList<>();
JsonArray array = value.getAsJsonArray();
if (null != array) {
for (JsonElement element : array) {
list.add(createHashMapFromJsonString(element.toString()));
}
map.put(key, list);
}
} else if (value.isJsonArray() && !value.toString().contains(":")) {
map.put(key, value.getAsJsonArray());
}
} else {
map.put(key, value.getAsString());
}
}
}
return map;
}
}
JSON of unknown format to HashMap
writing JSON And reading Json
public static JsonParser parser = new JsonParser();
public static void main(String args[]) {
writeJson("JsonFile.json");
readgson("JsonFile.json");
}
public static void readgson(String file) {
try {
System.out.println( "Reading JSON file from Java program" );
FileReader fileReader = new FileReader( file );
com.google.gson.JsonObject object = (JsonObject) parser.parse( fileReader );
Set <java.util.Map.Entry<String, com.google.gson.JsonElement>> keys = object.entrySet();
if ( keys.isEmpty() ) {
System.out.println( "Empty JSON Object" );
}else {
Map<String, Object> map = json_UnKnown_Format( keys );
System.out.println("Json 2 Map : "+map);
}
} catch (IOException ex) {
System.out.println("Input File Does not Exists.");
}
}
public static Map<String, Object> json_UnKnown_Format( Set <java.util.Map.Entry<String, com.google.gson.JsonElement>> keys ){
Map<String, Object> jsonMap = new HashMap<String, Object>();
for (Entry<String, JsonElement> entry : keys) {
String keyEntry = entry.getKey();
System.out.println(keyEntry + " : ");
JsonElement valuesEntry = entry.getValue();
if (valuesEntry.isJsonNull()) {
System.out.println(valuesEntry);
jsonMap.put(keyEntry, valuesEntry);
}else if (valuesEntry.isJsonPrimitive()) {
System.out.println("P - "+valuesEntry);
jsonMap.put(keyEntry, valuesEntry);
}else if (valuesEntry.isJsonArray()) {
JsonArray array = valuesEntry.getAsJsonArray();
List<Object> array2List = new ArrayList<Object>();
for (JsonElement jsonElements : array) {
System.out.println("A - "+jsonElements);
array2List.add(jsonElements);
}
jsonMap.put(keyEntry, array2List);
}else if (valuesEntry.isJsonObject()) {
com.google.gson.JsonObject obj = (JsonObject) parser.parse(valuesEntry.toString());
Set <java.util.Map.Entry<String, com.google.gson.JsonElement>> obj_key = obj.entrySet();
jsonMap.put(keyEntry, json_UnKnown_Format(obj_key));
}
}
return jsonMap;
}
#SuppressWarnings("unchecked")
public static void writeJson( String file ) {
JSONObject json = new JSONObject();
json.put("Key1", "Value");
json.put("Key2", 777); // Converts to "777"
json.put("Key3", null);
json.put("Key4", false);
JSONArray jsonArray = new JSONArray();
jsonArray.put("Array-Value1");
jsonArray.put(10);
jsonArray.put("Array-Value2");
json.put("Array : ", jsonArray); // "Array":["Array-Value1", 10,"Array-Value2"]
JSONObject jsonObj = new JSONObject();
jsonObj.put("Obj-Key1", 20);
jsonObj.put("Obj-Key2", "Value2");
jsonObj.put(4, "Value2"); // Converts to "4"
json.put("InnerObject", jsonObj);
JSONObject jsonObjArray = new JSONObject();
JSONArray objArray = new JSONArray();
objArray.put("Obj-Array1");
objArray.put(0, "Obj-Array3");
jsonObjArray.put("ObjectArray", objArray);
json.put("InnerObjectArray", jsonObjArray);
Map<String, Integer> sortedTree = new TreeMap<String, Integer>();
sortedTree.put("Sorted1", 10);
sortedTree.put("Sorted2", 103);
sortedTree.put("Sorted3", 14);
json.put("TreeMap", sortedTree);
try {
System.out.println("Writting JSON into file ...");
System.out.println(json);
FileWriter jsonFileWriter = new FileWriter(file);
jsonFileWriter.write(json.toJSONString());
jsonFileWriter.flush();
jsonFileWriter.close();
System.out.println("Done");
} catch (IOException e) {
e.printStackTrace();
}
}
Here is a sample I wrote shows how I parse a json and mess every number inside it:
public class JsonParser {
public static Object parseAndMess(Object object) throws IOException {
String json = JsonUtil.toJson(object);
JsonNode jsonNode = parseAndMess(json);
if(null != jsonNode)
return JsonUtil.toObject(jsonNode, object.getClass());
return null;
}
public static JsonNode parseAndMess(String json) throws IOException {
JsonNode rootNode = parse(json);
return mess(rootNode, new Random());
}
private static JsonNode parse(String json) throws IOException {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
JsonNode rootNode = mapper.readTree(json);
return rootNode;
}
private static JsonNode mess(JsonNode rootNode, Random rand) throws IOException {
if (rootNode instanceof ObjectNode) {
Iterator<Map.Entry<String, JsonNode>> fieldsIterator = rootNode.fields();
while (fieldsIterator.hasNext()) {
Map.Entry<String, JsonNode> field = fieldsIterator.next();
replaceObjectNode((ObjectNode) rootNode, field, rand);
}
} else if (rootNode instanceof ArrayNode) {
ArrayNode arrayNode = ((ArrayNode) rootNode);
replaceArrayNode(arrayNode, rand);
}
return rootNode;
}
private static void replaceObjectNode(ObjectNode rootNode, Map.Entry<String, JsonNode> field, Random rand)
throws IOException {
JsonNode childNode = field.getValue();
if (childNode instanceof IntNode) {
(rootNode).put(field.getKey(), rand.nextInt(1000));
} else if (childNode instanceof LongNode) {
(rootNode).put(field.getKey(), rand.nextInt(1000000));
} else if (childNode instanceof FloatNode) {
(rootNode).put(field.getKey(), format(rand.nextFloat()));
} else if (childNode instanceof DoubleNode) {
(rootNode).put(field.getKey(), format(rand.nextFloat()));
} else {
mess(childNode, rand);
}
}
private static void replaceArrayNode(ArrayNode arrayNode, Random rand) throws IOException {
int arrayLength = arrayNode.size();
if(arrayLength == 0)
return;
if (arrayNode.get(0) instanceof IntNode) {
for (int i = 0; i < arrayLength; i++) {
arrayNode.set(i, new IntNode(rand.nextInt(10000)));
}
} else if (arrayNode.get(0) instanceof LongNode) {
arrayNode.removeAll();
for (int i = 0; i < arrayLength; i++) {
arrayNode.add(rand.nextInt(1000000));
}
} else if (arrayNode.get(0) instanceof FloatNode) {
arrayNode.removeAll();
for (int i = 0; i < arrayLength; i++) {
arrayNode.add(format(rand.nextFloat()));
}
} else if (arrayNode.get(0) instanceof DoubleNode) {
arrayNode.removeAll();
for (int i = 0; i < arrayLength; i++) {
arrayNode.add(format(rand.nextFloat()));
}
} else {
for (int i = 0; i < arrayLength; i++) {
mess(arrayNode.get(i), rand);
}
}
}
public static void print(JsonNode rootNode) throws IOException {
System.out.println(rootNode.toString());
}
private static double format(float a) {
return Math.round(a * 10000.0) / 100.0;
}
}
Would you be satisfied with a Map from Jackson?
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.readValue(jsonString, new TypeReference<HashMap<String,Object>>(){});
Or maybe a JsonNode?
JsonNode jsonNode = objectMapper.readTree(String jsonString)

Access nested JSON object value using java

{
"files": {
"f1.png": {
"intext": "A",
"inval": 0,
"inbinary": false
},
"f2.png": {
"intext": "A",
"inval": 0,
"inbinary": true
}
}
}
How to access value of inval when the f1.png value is not fixed. i.e. the name of file can be anything, its not known so how can I access value for inval field for various files in this JSON using Java?
Please try below code,
import org.json.JSONException;
import org.json.JSONObject;
public static void main(String[] args) {
String jsonString = "{\"files\": {\"f1.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": false}, \"f2.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": true}}}";
try {
JSONObject jsonObject =new JSONObject(jsonString);
JSONObject jsonChildObject = (JSONObject)jsonObject.get("files");
Iterator iterator = jsonChildObject.keys();
String key = null;
while(iterator.hasNext()){
key = (String)iterator.next();
System.out.println("inval value: "+((JSONObject)jsonChildObject.get(key)).get("inval"));
}
}
catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Hope it solves your issue
Using Jackson and JsonNode, you'd do:
private static final ObjectReader READER = new ObjectMapper()
.getReader;
// blah
// read the node
final JsonNode node = READER.readTree(fromWhatever);
// access the inner "files" member
final JsonNode filesNode = node.get("files");
to access the inner object.
Then to walk the filesNode object you'd do:
final Iterator<Map.Entry<String, JsonNode>> iterator = filesNode.fields();
Map.Entry<String, JsonNode> entry;
while (iterator.hasNext()) {
entry = iterator.next();
// the "inval" field is entry.getValue().get("inval")
}
If you can use this project this becomes more simple:
// or .fromFile(), .fromReader(), others
final JsonNode node = JsonLoader.fromString(whatever);
final Map<String, JsonNode> map = JacksonUtils.nodeToMap(node.get("files"));
// walk the map
You can use JsonPath library to access child elements.
https://github.com/json-path/JsonPath
It can be as simple as
List<String> names = JsonPath.read(json, "$.files.*);
With some modifications.
You can use ObjectMapper.
First create a class Image.
import lombok.Data;
#Data
public class Image {
private String intext;
private Integer inval;
private Boolean inbinary;
}
and convert to Map
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonString =
"{\"files\": {\"f1.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": false}, \"f2.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": true}}}";
final Map<String, Map<String, Image>> output =
objectMapper.readValue(
jsonString, new TypeReference<Map<String, Map<String, Image>>>() {});
Thanks.
for nested array type,
{
"combo": [
{"field":"divisions"},
{"field":"lob"}
]
}
below code will help.
Iterator<?> keys = jsnObj.keys();
while(keys.hasNext()) {
String key = (String) keys.next();
JSONArray list = (JSONArray) jsnObj.get(key);
if(list==null || list.length()==0)
return null;
List<String> comboList = new ArrayList<String>();
for(int i=0;i<list.length();i++){
JSONObject jsonObject = (JSONObject)list.get(i);
if(jsonObject==null)
continue;
comboList.add((String)jsonObject.get("fields"));
}
}

Categories

Resources