I am trying to map a JSON file into Java objects using Jackson library. This json file is a multi-level file that can be found here:
https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson
It is the list of earthquakes that happened in the last 30 days in the US.
Here is the structure of this son file: https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php
Now, I wrote a Java program that is reading fields from this file, specifically I am trying to access the field which is under features -> properties -> place (e.g. from the original file "place":"17km NW of Pinnacles, CA")). When I get to the properties field I can read it as a LinkedHashMap, but the next level, so the keys and values of this LinkedHashMap are being read as Strings:
for example this is one of the values : {type=Point, coordinates=[-121.2743333, 36.6375, 8.61]}
I WANT TO READ THESE VALUES AS ANOTHER OBJECT (NOT STRING, MAP MAYBE?) SO I COULD EXTRACT FURTHER DATA FROM IT.
Here is my class:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.ObjectMapper;
#JsonIgnoreProperties(ignoreUnknown = true)
public class ReadJSONFile {
private StringBuffer stringBuffer = new StringBuffer();
public void convert_json_to_java() throws Exception {
String url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
while ((inputLine = in .readLine()) != null) {
stringBuffer.append(inputLine);
stringBuffer.append("\n");
} in.close();
}
#SuppressWarnings("unchecked")
public void map_to_object() throws Exception {
ObjectMapper om = new ObjectMapper();
//ignore fields that are not formatted properly
om.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Map<Object, Object> resultMap = om.readValue(stringBuffer.toString(), Map.class);
ArrayList<Object> featuresArrayList = (ArrayList<Object>) resultMap.get("features");
for(Object o : featuresArrayList) {
LinkedHashMap<Object, Object> propertiesMap = (LinkedHashMap<Object, Object>) o;
for(Map.Entry<Object, Object> entry : propertiesMap.entrySet()) {
//HERE IS THE PROBLEM, THE VALUES OF THIS MAP (SECOND OBJECT) IS BEING READ AS A STRING
//WHILE SOME VALUES ARE NOT A STRING:
//e.g. {type=Point, coordinates=[-121.2743333, 36.6375, 8.61]}
//AND I WANT TO READ IT AS A MAP OR ANY OTHER OBJECT THAT WOULD ALLOW ME TO ACCESS THE DATA
String propertiesMapValues = entry.getValue().toString();
}
}
}
}
Main.java
public class Main {
public static void main(String[] args) throws Exception {
ReadJSONFile rjf = new ReadJSONFile();
rjf.convert_json_to_java();
rjf.map_to_object();
}
}
Maven dependency: https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-mapper-asl
When I try casting this last object to anything else than String, the program gives me exception (can't cast String to another object). Did I do something wrong? Can someone tell me what can I do to access those fields without modifying Strings (e.g. splitting them into arrays etc.)?
Actually your code works but it could be a bit simplified. The method convert_json_to_java is unnecessary, you can pass the URL directly to the ObjectMapper.
The values in the map are not read as Strings, but you are converting them to Strings by calling toString(), which is defined for all objects. Acctual types can be Map, List, String, Integer etc., depending on the JSON content. Working with a generic map is indeed a bit annoying, so I would suggest you converting values to structured objects. GeoJSON is an open standard, so there are open-source libraries facilitating using it, e.g. geojson-jackson.
You would need to add a maven dependency:
<dependency>
<groupId>de.grundid.opendatalab</groupId>
<artifactId>geojson-jackson</artifactId>
<version>1.8.1</version>
</dependency>
Then the program could look something like:
import org.geojson.*
// ...
public class ReadJSONFile {
ObjectMapper om = new ObjectMapper();
public void mapToObject(String url) throws Exception {
Map<String, Object> resultMap = om.readValue(new URL(url), new TypeReference<Map<String, Object>>() {});
List<Feature> features = om.convertValue(resultMap.get("features"), new TypeReference<List<Feature>>() {});
for(Feature f : features) {
// Write the feature to the console to see how it looks like
System.out.println(om.writeValueAsString(f));
// Extract properties
Map<String,Object> properties = f.getProperties();
// ....
// Extract geometry
GeoJsonObject geometry = f.getGeometry();
if(geometry instanceof Point) {
Point p = (Point) geometry;
// do something with the point
} else if(geometry instanceof LineString) {
LineString mls = (LineString) geometry;
// ...
} else if(geometry instanceof MultiLineString) {
MultiLineString mls = (MultiLineString) geometry;
// ...
} else if(geometry instanceof MultiPoint) {
MultiPoint mp = (MultiPoint) geometry;
// ...
} else if(geometry instanceof Polygon) {
Polygon pl = (Polygon) geometry;
// ...
} else if(geometry != null) {
throw new RuntimeException("Unhandled geometry type: " + geometry.getClass().getName());
}
}
}
public static void main(String[] args) throws Exception {
ReadJSONFile rjf = new ReadJSONFile();
rjf.mapToObject("https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson");
}
}
I want to serialize a Map with Jackson.
The Date should be serialized as a timestamp, like all my other dates.
The following code renders the keys in the form "Tue Mar 11 00:00:00 CET 1952" (which is Date.toString()) instead of the timestamp.
Map<Date, String> myMap = new HashMap<Date, String>();
...
ObjectMapper.writeValue(myMap)
I assume this is because of type erasure and jackson doesn't know at runtime that the key is a Date. But I didn't find a way to pass a TypeReference to any writeValue method.
Is there a simple way to achieve my desired behaviour or are all keys always rendered as Strings by jackson?
Thanks for any hint.
The default map key serializer is StdKeySerializer, and it simply does this.
String keyStr = (value.getClass() == String.class) ? ((String) value) : value.toString();
jgen.writeFieldName(keyStr);
You could make use of the SimpleModule feature, and specify a custom key serializer, using the addKeySerializer method.
And here's how that could be done.
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.type.MapType;
import org.codehaus.jackson.map.type.TypeFactory;
public class CustomKeySerializerDemo
{
public static void main(String[] args) throws Exception
{
Map<Date, String> myMap = new HashMap<Date, String>();
myMap.put(new Date(), "now");
Thread.sleep(100);
myMap.put(new Date(), "later");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(myMap));
// {"Mon Jul 04 11:38:36 MST 2011":"now","Mon Jul 04 11:38:36 MST 2011":"later"}
SimpleModule module =
new SimpleModule("MyMapKeySerializerModule",
new Version(1, 0, 0, null));
module.addKeySerializer(Date.class, new DateAsTimestampSerializer());
MapType myMapType = TypeFactory.defaultInstance().constructMapType(HashMap.class, Date.class, String.class);
ObjectWriter writer = new ObjectMapper().withModule(module).typedWriter(myMapType);
System.out.println(writer.writeValueAsString(myMap));
// {"1309806289240":"later","1309806289140":"now"}
}
}
class DateAsTimestampSerializer extends JsonSerializer<Date>
{
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeFieldName(String.valueOf(value.getTime()));
}
}
Update for the latest Jackson (2.0.4):
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
public class CustomKeySerializerDemo
{
public static void main(String[] args) throws Exception
{
Map<Date, String> myMap = new HashMap<Date, String>();
myMap.put(new Date(), "now");
Thread.sleep(100);
myMap.put(new Date(), "later");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(myMap));
// {"2012-07-13T21:14:09.499+0000":"now","2012-07-13T21:14:09.599+0000":"later"}
SimpleModule module = new SimpleModule();
module.addKeySerializer(Date.class, new DateAsTimestampSerializer());
MapType myMapType = TypeFactory.defaultInstance().constructMapType(HashMap.class, Date.class, String.class);
ObjectWriter writer = new ObjectMapper().registerModule(module).writerWithType(myMapType);
System.out.println(writer.writeValueAsString(myMap));
// {"1342214049499":"now","1342214049599":"later"}
}
}
class DateAsTimestampSerializer extends JsonSerializer<Date>
{
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeFieldName(String.valueOf(value.getTime()));
}
}
As usual, Bruce's answer is right on the spot.
One additional thought is that since there is a global setting for serializing Date values as timestamps:
SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS
Maybe that should apply here as well. And/or at least use standard ISO-8601 format for text. The main practical issue there is that of backwards compatibility; however, I doubt that current use of plain toString() is very useful as it is neither efficient nor convenient (to read back the value).
So if you want, you might want to file a feature request; this sounds like sub-optimal handling of Map keys by Jackson.
Since Jackson 2.0 (maybe 1.9, too), WRITE_DATE_KEYS_AS_TIMESTAMPS can be used to change this particular behavior.
Usage example for ObjectMapper:
ObjectMapper m = new ObjectMapper().configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true);
and for ObjectWriter:
ObjectWriter w = mapper.with(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
I have to generate a json file containing indentation. I was using Jackson for this but it adds a space before the colon and I don't need it, so I decided to use Gson.
After changing the code, I figured out that by default Gson don't use indentation but Jackson does. Does anyone know if it is possible in Gson to get indentation and how to do it?
For generating the json file with Gson I made this:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder = gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
Writer writer = new FileWriter(propsFile);
gson.toJson(properties, writer);
You can only employ the default indentation provided by setPrettyPrinting() and you cannot change its size or indentation characters. Please refer to the official information about it: Compact Vs. Pretty Printing for JSON Output Format.
Possible duplicate of https://stackoverflow.com/a/41509714/4379906
Use Jackson and and configure it not to add spaces before colon:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
CustomPrettyPrinter.java:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import java.io.IOException;
class CustomPrettyPrinter extends DefaultPrettyPrinter {
public CustomPrettyPrinter() {
super();
}
public CustomPrettyPrinter(DefaultPrettyPrinter base) {
super(base);
}
#Override
public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException {
g.writeRaw(": ");
}
#Override
public DefaultPrettyPrinter createInstance() {
return new CustomPrettyPrinter(this);
}
}
UseJackson.java:
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
public class UseJackson {
public static void main(String[] args) throws IOException {
DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter(" ", DefaultIndenter.SYS_LF);
DefaultPrettyPrinter printer = new CustomPrettyPrinter();
printer.indentArraysWith(indenter);
printer.indentObjectsWith(indenter);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDefaultPrettyPrinter(printer);
Properties properties = new Properties();
properties.put("foo", "3");
properties.put("bar", "4");
objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File("a.json"), properties);
}
}
Use Gson and configure indentation:
UseGson.java:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class UseGson {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.put("foo", "3");
properties.put("bar", "4");
JsonWriter jsonWriter = new JsonWriter(new FileWriter("a.json"));
jsonWriter.setIndent(" ");
Gson gson = new GsonBuilder().serializeNulls().create();
gson.toJson(properties, Properties.class, jsonWriter);
jsonWriter.close();
}
}
I'm having a hash map with set of keys and values.
I would like to convert it to a json format and print the entire string.
I don't like to create a file and need to dynamically print the string in the screen. i'm using fasterxml api. (http://wiki.fasterxml.com/JacksonInFiveMinutes)
Please let me know how to do it.
Please look at the following:
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
public static void main(String[] args) throws JsonProcessingException {
Map<String, Object> map = new HashMap<String, Object>();
map.put( "language", "Java" );
map.put( "year", 2016 );
map.put( "isObjectOriented", true );
ObjectMapper mapper = new ObjectMapper();
String jsonInString = mapper.writeValueAsString(map);
System.out.printf( "JSON: %s", jsonInString );
}
}
I have used following jars:
jackson-core-2.7.4.jar, jackson-databind-2.7.4.jar and jackson-annotations-2.7.4.jar. Hope it helps.
What is the best way to convert a JSON code as this:
{
"data" :
{
"field1" : "value1",
"field2" : "value2"
}
}
in a Java Map in which one the keys are (field1, field2) and the values for those fields are (value1, value2).
Any ideas? Should I use Json-lib for that? Or better if I write my own parser?
I hope you were joking about writing your own parser. :-)
For such a simple mapping, most tools from http://json.org (section java) would work.
For one of them (Jackson https://github.com/FasterXML/jackson-databind/#5-minute-tutorial-streaming-parser-generator), you'd do:
Map<String,Object> result =
new ObjectMapper().readValue(JSON_SOURCE, HashMap.class);
(where JSON_SOURCE is a File, input stream, reader, or json content String)
Using the GSON library:
import com.google.gson.Gson;
import com.google.common.reflect.TypeToken;
import java.lang.reclect.Type;
Use the following code:
Type mapType = new TypeToken<Map<String, Map>>(){}.getType();
Map<String, String[]> son = new Gson().fromJson(easyString, mapType);
I like google gson library.
When you don't know structure of json. You can use
JsonElement root = new JsonParser().parse(jsonString);
and then you can work with json. e.g. how to get "value1" from your gson:
String value1 = root.getAsJsonObject().get("data").getAsJsonObject().get("field1").getAsString();
Use JSON lib E.g. http://www.json.org/java/
// Assume you have a Map<String, String> in JSONObject jdata
#SuppressWarnings("unchecked")
Iterator<String> nameItr = jdata.keys();
Map<String, String> outMap = new HashMap<String, String>();
while(nameItr.hasNext()) {
String name = nameItr.next();
outMap.put(name, jdata.getString(name));
}
My post could be helpful for others, so imagine you have a map with a specific object in values, something like that:
{
"shopping_list":{
"996386":{
"id":996386,
"label":"My 1st shopping list",
"current":true,
"nb_reference":6
},
"888540":{
"id":888540,
"label":"My 2nd shopping list",
"current":false,
"nb_reference":2
}
}
}
To parse this JSON file with GSON library, it's easy :
if your project is mavenized
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
Then use this snippet :
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
//Read the JSON file
JsonElement root = new JsonParser().parse(new FileReader("/path/to/the/json/file/in/your/file/system.json"));
//Get the content of the first map
JsonObject object = root.getAsJsonObject().get("shopping_list").getAsJsonObject();
//Iterate over this map
Gson gson = new Gson();
for (Entry<String, JsonElement> entry : object.entrySet()) {
ShoppingList shoppingList = gson.fromJson(entry.getValue(), ShoppingList.class);
System.out.println(shoppingList.getLabel());
}
The corresponding POJO should be something like that :
public class ShoppingList {
int id;
String label;
boolean current;
int nb_reference;
//Setters & Getters !!!!!
}
Hope it helps !
With google's Gson 2.7 (probably earlier versions too, but I tested 2.7) it's as simple as:
Map map = gson.fromJson(json, Map.class);
Which returns a Map of type class com.google.gson.internal.LinkedTreeMap and works recursively on nested objects.
This way its works like a Map...
JSONObject fieldsJson = new JSONObject(json);
String value = fieldsJson.getString(key);
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.1</version>
</dependency>
I do it this way. It's Simple.
import java.util.Map;
import org.json.JSONObject;
import com.google.gson.Gson;
public class Main {
public static void main(String[] args) {
JSONObject jsonObj = new JSONObject("{ \"f1\":\"v1\"}");
#SuppressWarnings("unchecked")
Map<String, String> map = new Gson().fromJson(jsonObj.toString(),Map.class);
System.out.println(map);
}
}
java.lang.reflect.Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
Gson gson = new Gson();
Map<String, Object> categoryicons = gson.fromJson(json, mapType );
If you're using org.json, JSONObject has a method toMap().
You can easily do:
Map<String, Object> myMap = myJsonObject.toMap();
The JsonTools library is very complete. It can be found at Github.
Try this code:
public static Map<String, Object> convertJsonIntoMap(String jsonFile) {
Map<String, Object> map = new HashMap<>();
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
mapper.readValue(jsonFile, new TypeReference<Map<String, Object>>() {
});
map = mapper.readValue(jsonFile, new TypeReference<Map<String, String>>() {
});
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
One more alternative is json-simple which can be found in Maven Central:
(JSONObject)JSONValue.parse(someString); //JSONObject is actually a Map.
The artifact is 24kbytes, doesn't have other runtime dependencies.
If you need pure Java without any dependencies, you can use build in Nashorn API from Java 8. It is deprecated in Java 11.
This is working for me:
...
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
...
public class JsonUtils {
public static Map parseJSON(String json) throws ScriptException {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("javascript");
String script = "Java.asJSONCompatible(" + json + ")";
Object result = engine.eval(script);
return (Map) result;
}
}
Sample usage
JSON:
{
"data":[
{"id":1,"username":"bruce"},
{"id":2,"username":"clark"},
{"id":3,"username":"diana"}
]
}
Code:
...
import jdk.nashorn.internal.runtime.JSONListAdapter;
...
public static List<String> getUsernamesFromJson(Map json) {
List<String> result = new LinkedList<>();
JSONListAdapter data = (JSONListAdapter) json.get("data");
for(Object obj : data) {
Map map = (Map) obj;
result.add((String) map.get("username"));
}
return result;
}
JSON to Map always gonna be a string/object data type. i have GSON lib from google.
Gson library working with string not for complex objects you need to do something else
Try this piece of code, it worked for me,
Map<String, Object> retMap = new Gson().fromJson(
myJsonString, new TypeToken<HashMap<String, Object>>() {}.getType()
);
import net.sf.json.JSONObject
JSONObject.fromObject(yourJsonString).toMap
Underscore-java library can convert json string to hash map. I am the maintainer of the project.
Code example:
import com.github.underscore.U;
import java.util.*;
public class Main {
#SuppressWarnings("unchecked")
public static void main(String[] args) {
String json = "{"
+ " \"data\" :"
+ " {"
+ " \"field1\" : \"value1\","
+ " \"field2\" : \"value2\""
+ " }"
+ "}";
Map<String, Object> data = (Map) U.get((Map<String, Object>) U.fromJson(json), "data");
System.out.println(data);
// {field1=value1, field2=value2}
}
}
JSON to Map always gonna be a string/object data type. i haved GSON lib from google.
works very well and JDK 1.5 is the min requirement.