I have a this map:
HashMap<LatLng,ArrayList<String>> dictionary = new HashMap<LatLng,ArrayList<String>>() ;
and I'm trying to flush it to disk in JSON format using jackson like this:
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(new File(MyApplication.getOfflineData()+"/Dictionary.json"), dictionary);
this is how is written the json file:
"lat/lng: (39.151783,20.97455)": [
"/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic1.nomedia",
"/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic2.nomedia",
"/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic3.nomedia",
"/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic4.nomedia"
]
I think this is not correct. I think the "lat/lng": shouldn't exist, how can I correct this?
Thank you for your time.
When you are using complex POJO class as a key in a Map, Jackson have to generate property name using this class. Simplest implementation is using toString method. In JSON we are not able to create complex property name. Object can not be a property. To solve your problem I propose to create new POJO class which contains LatLng and List<String> properties. See example:
class Root {
private LatLng latLng;
private List<String> values;
//getters,setters
}
Now, your JSON will be looking like this:
{
"latLng" : {
"lat" : "39.151783",
"lng" : "20.97455"
},
"values" : [ "/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic1.nomedia", "/mnt/sdcard/Android/data/example.perifereia_hpeirou/files/lala/photos/pic2.nomedia" ]
}
Just flush out the values of the HashMap.
mapper.writeValue(new File(MyApplication.getOfflineData()+"/Dictionary.json"), dictionary.values())
Or override the toString() of LatLng if you want to customise representation of LatLng.
Related
This is similar to this question but it's a little different.
Let's say I have a json document defined like this:
[
{ "type" : "Type1",
"key1" : "value1" },
{ "type" : "Type2",
"key2" : "value2" }
]
I want to read this json document into a list of strings (List<String>). I only want to read the outer-most list into the Java List, the json objects inside the list should be left as-is inside the list. The result should be equivalent to this (I ignore newlines etc):
var myList = List.of("{\"type\": \"Type1\", \"key1\": \"value1\"}, {\"type\": \"Type2\", \"key2\": \"value2\"}")
Note that I don't want to create any DTO to hold some intermediate representation. I just want everything below the "list" to be represented "as-is".
How can I achieve this?
I'm using Jackson 2.12.1.
If you don't want to hold intermediate representation in a DTO, then one way in which the required deserialization can be achieved is:
// Create a ObjectMapper (of type com.fasterxml.jackson.databind.ObjectMapper)
ObjectMapper mapper = new ObjectMapper();
// Read the json string into a List. This will be deserialized as a collection of LinkedhashMap
List<LinkedHashMap> list = mapper.readValue(getInputString(), List.class);
//Iterate over the deserialized collection and create a JSONObject from every LinkedHashMap
List<String> result = list.stream()
.map(map -> new JSONObject(map).toString())
.collect(Collectors.toList());
This will produce:
[{"key1":"value1","type":"Type1"}, {"key2":"value2","type":"Type2"}]
Downside of this approach is, it takes a hit on performance.
I have JSON with objects in specific order:
{
"Aaa": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
"Test2"
]
}
},
"Bbb": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
"Test2"
]
}
},
"Ddd": {
"Langs": {
"Val": [
"Test"
],
"Pro": [
]
}
},
}
And I would like to add new object Ccc between Bbb and Ddd. I tried to configure object mapper like this:
ObjectMapper mapper = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
and then print with this code, but Ccc ends at the end of file.
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
//Write whole JSON in FILE
String finalJson = mapper.writer(prettyPrinter).writeValueAsString(rootFlores);
finalJson = finalJson.replaceAll("\\[ ]", "[" + System.lineSeparator() + " ]");
finalJson = finalJson.replaceAll("/", "\\\\/");
Files.write(Paths.get("DictionaryFlores_new.json"), Collections.singleton(finalJson));
Is here a way how to print JSON ordered?
Jackson deserialization/serialization does not sort properties
According to this answer, the Jackson SORT_PROPERTIES_ALPHABETICALLY only applies to POJO properties, not Maps. In JSON there is no difference between a Map and an Object, so you need to set the order in the Map first by using a LinkedHashMap or TreeMap
By definition, the keys of an object are unordered. I guess some libraries could offer an option to control the order of the keys when stringifying, but I wouldn't count on it.
When you need a certain order in json, you need to use an array. Of course, then you'd have to move the keys to a property in the child objects, and then the resulting array could only be indexed by number (not by the key). So then you might have to do additional processing to covert the data structure in the JSON to the data structure you really want to process.
Since you seems ready to use regex to update a JSON, I would suggest a "safer" approach. Don't try to create a pattern that would unsure that you don't update a value somewhere.
Iterate you values, on object at the time. Stringify the object and append the String yourself. That way, you are in charge of the object order. Example :
StringBuilder sb = new StringBuilder("{");
List<JsonPOJO> list = new ArrayList<>();
//populate the list
for(JsonPOJO pojo : list){
sb.append(pojo.stringify()).append(",");
}
sb.setLength(sb.length() - 1); //remove the last commma
sb.append("}");
Here, you are only managing the comma between each JSON object, not create the "complex" part about the JSON. And you are in full control of the order of the value in the String representation, it will only depend on the way you populate the List.
Note: sorry for the "draft" code, I don't really have access to my system here so just write this snippet to give you a basic idea on how to "manage" a JSON without having to recreating an API completely.
Note2: I would note suggest this unless this looks really necessary. As you mention in a comment, you are have only the problem with one key where you already have a JSON with 80000 keys, so I guess this is a "bad luck" scenario asking for last resort solution ;)
I am trying to parse a JSON .txt file into a JAVA object using GSON. The JSON file has the following structure:
{
"event0" : {
"a" : "abc",
"b" : "def"
},
"event1" : {
"a" : "ghi",
"b" : "jkl",
"c" : "mno"
}
}
I have read the text file into a String called dataStr. I want to use the fromJson method to capture the events into the following JAVA class:
public class Event {
private String a;
private String b;
private String c;
public Event() {}
}
The problem is that the JSON might have one extra field "c" in some of the elements. I want to parse all the events into Event class objects, and for the cases where there is no "c" field, I want to make it null or zero in my object. It is not known beforehand which of the elements will have the "c" field.
Specifically, I was not able to figure out how to handle one extra field in some of the JSON elements. I want to do something along the lines of:
Gson gson = new Gson();
ArrayList<Event> events = gson.fromJson(dataStr, Event.class);
But I am stuck with first, how to iterate over the events in the Json file, and secondly, how to handle some occasional missing fields into the same Event object. I would really appreciate a kick in the right direction. Thank you all.
I am fairly new to JSON parsing, and might have missed something in the following answers:
Using Gson to convert Json into Java Object
Mapping JSON into POJO using Gson
Using gson to parse json to java object
Parse JSON into a java object
How to parse a json file into a java POJO class using GSON
I'm not sure if I understood your question right. As per my understanding, you are trying to convert a json object with an extra field which is not available in the java class. Frankly, I don't understand why you want that or if it's possible to start with. You can have a workaround by converting the json to Map.
Map map = gson.fromJson(jsonString, Map.class);
Gson automatically do that for you.
So, if you have a class "Alpha" with 3 fields ("a", "b" and "c") and you try to work on a json object that has 2 fields with names that match with Alpha's "a" and "b", Gson will fill "a" and "b" with json file's value and "c" will automatically set as null.
So, in your case, if you write this:
ArrayList<Event> events = gson.fromJson(dataStr, Event.class);
And in your json there are events with only 2 fields (that match with any Event's class fields) and events with all fields set, you will get a list of Events with no errors. Maybe you'll get some fields null, but the code will work.
I hope to be helpful! Ask for further informations, if you want to!
EDIT
Note that your json file has not to be .txt but .json instead!
First I believe your JSON should look like this:
{
"events": [
{
"name": "event0",
"a": "abc",
"b": "def"
},
{
"name": "event1",
"a": "abc",
"b": "def",
"c": "mno"
}
]
}
This will need two classes for your model:
public List<Event> events = null;
public class Event {
public String name;
public String a;
public String b;
public String c;
}
And then then with GSON
Events events = gson.fromJson(jsonData, Events.class);
Also I recommend to always use an online validator for JSON so you are sure your JSON structure is correct before coding against it.
https://jsonlint.com/
Or for formate the JSON:
http://jsonprettyprint.com/
Also this website can create the Java classes for you from either a JSON Schema or by using an example file.
http://www.jsonschema2pojo.org/
Try the below code snippet:
Gson gson = new Gson();
ArrayList<Event> events = gson.fromJson(dataStr, new TypeToken<ArrayList<Event>>(){}.getType());
In the source code of Gson has a very clear explain
if i have this json
{
"aggregations" : {
"cityAgg": {
"'buckets": {
"buckets" : [
{},{},{}
]
}
}
}
}
I can extract the last buckets array like this using simple json
JSONObject aggregations = (JSONObject)jsonResponse.get("aggregations");
JSONObject cityAgg = (JSONObject)aggregations.get("cityAgg");
JSONObject buckets = (JSONObject) cityAgg.get("buckets");
JSONArray result = (JSONArray) buckets.get("buckets");
but it is boring to make object for each json level, isn't there a better/easier way to do that?
something maybe like:
jsonResponse.get("aggregation/cityAgg/buckets/buckets")
You can use Gson provided by Google to format json,convert Object to Json and vice versa.
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source-code of.
Can go through samples here :
Gson Usage
Example
{
"aggregations" : {
"cityAgg": {
"'buckets": {
"buckets" : [
{},{},{}
]
}
}
}
}
Create a model which has such depth hierarchy
class Aggregations{
private CityAgg cityAgg;
//getters and setters
}
class CityAgg{
private Bucket buckets;
//getters and setters
}
class Bucket{
//some properties
}
you'll use the parent class to get all properties loaded inside it
I have a Java object Results:
public class MetaData {
private List<AttributeValue<String,Object>> properties
private String name
...
... getters/setters ...
}
The AttributeValue class is a generic key-value class. It's possible different AttributeValue's are nested. The (value) Object will then be another AttributeValue and so forth.
Due to legacy reasons the structure of this object cannot be altered.
I have my JSON, which I try to map to this object.
All goes well for the regular properties. Also the first level of the list is filled with AttributeValues.
The problem is the Object. Jackson doesn't know how to interpret this nested behavior and just makes it a LinkedHashMap.
I'm looking for a way to implement custom behavior to tell Jackson this has to be a AttributeValue-object instead of the LinkedHashMap.
This is how I'm currently converting the JSON:
ObjectMapper om = new ObjectMapper();
MetaData metaData = om.readValue(jsonString, new TypeReference<MetaData>(){});
And this is example JSON. (this is obtained by serializing an existing MetaData object to JSON, I have complete control over this syntax).
{
"properties":[
{
"attribute":"creators",
"value":[
{
"attribute":"creator",
"value":"user1"
},{
"attribute":"creator",
"value":"user2"
}
]
},{
"attribute":"type",
"value": "question"
}
],
"name":"example"
}
(btw: I've tried the same using GSON, but then the object is a StringMap and the problem is the same. Solutions using GSON are also welcome).
edit In Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap there is a comment from StaxMan:
"LinkedHashMap is only returned when type information is missing (or if Object.class is defined as type)."
The latter seems to be the issue here. Is there a way I can override this?
If you have control over the serialization, try calling enableDefaultTyping() on your mapper.
Consider this example:
Pair<Integer, Pair<Integer, Integer>> pair = new Pair<>(1, new Pair<>(1, 1));
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pair);
Pair result = mapper.readValue(str, Pair.class);
Without enableDefaultTyping(), I would have str = {"k":1,"v":{"k":1,"v":1}} which would deserialize to a Pair with LinkedHashMap.
But if I enableDefaultTyping() on mapper, then str = {"k":1,"v":["Pair",{"k":1,"v":1}]} which then perfectly deserializes to Pair<Integer, Pair<...>>.