I am trying to parse using Jackson mapper to parse big JSON to java object. I have very big JSON but came across this little piece in it and not sure how to parse.
Here is the JSON, the format of it looks little different. I am trying to understand how can I parse it to an object.
{
"coordinates": [
[
[
-72.943068,
45.842298
],
[
-72.943075,
45.841859
]
]
]
}
I don't understand which format it is in, and how I can parse it to object.
It depends how really big is your JSON. If you can load it to memory, you can use the simplest way:
Solution 1:
POJO class:
class CoordinatesContainer {
private double[][][] coordinates;
public double[][][] getCoordinates() {
return coordinates;
}
public void setCoordinates(double[][][] coordinates) {
this.coordinates = coordinates;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder(1024);
for (double[] arrayItem : coordinates[0]) {
builder.append(Arrays.toString(arrayItem));
builder.append(", ");
}
return builder.toString();
}
}
Usage:
ObjectMapper mapper = new ObjectMapper();
CoordinatesContainer coordinatesContainer = mapper.readValue(json, CoordinatesContainer.class);
System.out.println(coordinatesContainer);
Above program prints:
[-72.943068, 45.842298], [-72.943075, 45.841859]
Solution 2:
But if your JSON is really big and you are not able to load it to memory, you should consider Jackson Streaming API. In this case you should not create POJO class and try to process each element "node" by "node":
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class JsonProgram {
public static void main(String[] args) throws IOException {
File json = new File("/x/data.json");
JsonFactory jsonFactory = new JsonFactory();
JsonParser parser = jsonFactory.createParser(json);
// Skip all elements to first array
while (parser.nextToken() != JsonToken.START_ARRAY) {
}
parser.nextToken();
// First level
while (parser.nextToken() != JsonToken.END_ARRAY) {
// Skip inner start array element
parser.nextToken();
System.out.println();
System.out.println("NEXT ARRAY NODE");
BigDecimal first = parser.getDecimalValue();
// Go to second value
parser.nextToken();
BigDecimal second = parser.getDecimalValue();
// Skip inner end array element
parser.nextToken();
// Handle array item
System.out.println("First: " + first.toString());
System.out.println("Second: " + second.toString());
}
}
}
Above program prints:
NEXT ARRAY NODE
First: -72.943068
Second: 45.842298
NEXT ARRAY NODE
First: -72.943075
Second: 45.841859
In my examples I used Jackson in 2.2.3 version.
Create one json pojo mapper class
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"coordinates"
})
public class Example {
#JsonProperty("coordinates")
private List<List<List<Double>>> coordinates = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("coordinates")
public List<List<List<Double>>> getCoordinates() {
return coordinates;
}
#JsonProperty("coordinates")
public void setCoordinates(List<List<List<Double>>> coordinates) {
this.coordinates = coordinates;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Then convert jsonString to pojo
Gson gson = new GsonBuilder().create();
Example r = gson.fromJson(jsonString, Example.class);
Related
I have a simple class in java like below:
class Simple {
private String name;
private String email;
}
I want to have behaviour of java.util.List<Simple> and Simple both according to input data that my program receives.
i.e.
Case 1::
if my program receives below kind of json-array input
{"simple" : [ {"name":"a", "email" : "a#z.com"}, {"name":"b", "email" : "b#z.com"} ]}
I need to parse it using List<Simple>
Case 2::
if my program receives below kind of json-object input
{"simple" : {"name":"c", "email" : "c#z.com"} }
I need to parse it using Simple
Note: I have tried using JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, but the problem is it is basically converting single value also into json-array at the time of writing json.
I need to persist json as it is, is there any other way to achieve this?
To avoid any Jackson customisation I would create wrapper class with an Object simple property. We can add two extra checking methods and two extra casting methods. It will allow Jackson to do it's logic and in runtime we can check what actually we have:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
public class DateApp {
private final static JsonMapper JSON_MAPPER = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
public static void main(String[] args) throws Exception {
Simple object = new Simple("John", "john#doe.com");
SimpleWrapper wrapper = new SimpleWrapper();
wrapper.setSimple(object);
serializeAndDeserialize(wrapper);
wrapper.setSimple(Collections.singletonList(object));
serializeAndDeserialize(wrapper);
}
private static void serializeAndDeserialize(SimpleWrapper wrapper) throws JsonProcessingException {
String json = JSON_MAPPER.writeValueAsString(wrapper);
System.out.println("JSON:");
System.out.println(json);
wrapper = JSON_MAPPER.readValue(json, SimpleWrapper.class);
System.out.println("Wrapper:");
System.out.println(wrapper);
}
}
#Data
class SimpleWrapper {
private Object simple;
#JsonIgnore
public boolean isSimpleObject() {
return simple instanceof Simple;
}
#JsonIgnore
public boolean isSimpleList() {
return simple instanceof List;
}
#JsonIgnore
public Simple getSimpleAsObject() {
return (Simple) simple;
}
#JsonIgnore
public List<Simple> getSimpleAsList() {
return (List<Simple>) simple;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Simple {
private String name;
private String email;
}
Above code prints:
JSON:
{
"simple" : {
"name" : "John",
"email" : "john#doe.com"
}
}
Wrapper:
SimpleWrapper(simple={name=John, email=john#doe.com})
JSON:
{
"simple" : [ {
"name" : "John",
"email" : "john#doe.com"
} ]
}
Wrapper:
SimpleWrapper(simple=[{name=John, email=john#doe.com}])
You can use JsonNode.isArray() (or JsonNode.isObject()) to perform this check.
Then you can parse the node into a list with ObjectReader.readValue() or into a POJO using ObjectMapper.treeToValue().
String myJson = """
{"simple" : {"name":"c", "email" : "c#z.com"} }
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(myJson);
if (node.isArray()) {
ObjectReader reader = mapper.readerFor(new TypeReference<List<Simple>>() {});
List<Simple> list = reader.readValue(node);
// do something with a list
} else {
Simple pojo = mapper.treeToValue(node, Simple.class);
// do something else with a single object
}
Jackson is able to parse any json into a map where value is any object. you can then inquire on the type of the map value
Map<String, Object> map = new ObjectMapper().readValue(jsonInput, Map.class);
Object value = map.get("simple");
if (value instanceof Collection) { // will return false for null
Collection<Simple> simples = (Collection<Simple>)value;
}
else if (value instanceof Simple) {
Simple simple = (Simple)value;
}
else {
System.err.println("unrecognized");
}
You only need to read the first node, simple and check if it is an array - using isArray() method.
public class Main{
public static void main(String args[]) {
//String inputString = [your input];
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(inputString);
JsonNode simpleNode = root.findPath("simple");
if(simpleNode.isArray()) {
//you have an array
} else {
// you have an element
}
}
}
I am trying to parse the json array with same key value which looks something like:
Back End Response:"Countries":[{"state":"Queens Land "state":"Tasmania"}].
2.I have created classes to read back end response and mapping the values with faster XML, but only the last value in the array is getting copied, instead of entire array. This is how I created my Data Transfer Object classes.
Now the Test object contains Countries array, but only one of the State value is read. i.e
"Countries":["States":"Tasmania"].
Please excuse me for typos. can some one help, can some one suggest whats wrong with the bellow code..
private Class Test{
List<Countries> countries;
}
private class Countries{
private String States;
}
private class Mapper {
}
In my Mapper class reading the value using faster XML
Assume that your JSON payload is:
{
"Countries": [
{
"state": "Queens Land",
"state": "Tasmania"
}
]
}
According to RFC7159:
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
In your example, you have not unique names and most JSON parsers would skip repeated values and would take only one. So, if you can change backend response, just change it to:
{
"Countries": [
{
"state": "Queens Land"
},
{
"state": "Tasmania"
}
]
}
or
{
"Countries": [
"Queens Land",
"Tasmania"
]
}
But if you can not do that, you need to use Streaming API and implement your custom deserialiser. See below example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Test test = mapper.readValue(jsonFile, Test.class);
System.out.println(test);
}
}
class CountriesJsonDeserializer extends JsonDeserializer<Countries> {
#Override
public Countries deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<String> states = new ArrayList<>();
while (p.nextToken() != JsonToken.END_OBJECT) {
if (p.currentToken() == JsonToken.FIELD_NAME) {
if ("state".equalsIgnoreCase(p.getText())) {
p.nextToken();
states.add(p.getText());
}
}
}
Countries countries = new Countries();
countries.setStates(states);
return countries;
}
}
class Test {
#JsonProperty("Countries")
private List<Countries> countries;
public List<Countries> getCountries() {
return countries;
}
public void setCountries(List<Countries> countries) {
this.countries = countries;
}
#Override
public String toString() {
return "Test{" +
"countries=" + countries +
'}';
}
}
#JsonDeserialize(using = CountriesJsonDeserializer.class)
class Countries {
private List<String> states;
public List<String> getStates() {
return states;
}
public void setStates(List<String> states) {
this.states = states;
}
#Override
public String toString() {
return "Countries{" +
"states=" + states +
'}';
}
}
Above example prints:
Test{countries=[Countries{states=[Queens Land, Tasmania]}]}
See also:
Intro to the Jackson ObjectMapper
I use Jackson to serialize/deserialize JSON.
I have a List<String> in which all elements inside are already serialized in JSON format. I would like to generate a big JSON from that List.
In other word, I have:
List<String> a = new ArrayList<>();
a[0] = JSON_0
a[1] = JSON_1
...
a[N] = JSON_N
And I would like to render:
[
{JSON_0},
{JSON_1},
...
{JSON_N}
]
What is the best way to do so using Jackson?
Probably the simpler solution would be to create ArrayNode and use addRawValue method:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.util.RawValue;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ArrayNode nodes = mapper.getNodeFactory().arrayNode();
nodes.addRawValue(new RawValue("{}"));
nodes.addRawValue(new RawValue("true"));
nodes.addRawValue(new RawValue("{\"id\":1}"));
System.out.println(mapper.writeValueAsString(nodes));
}
}
Above code prints:
[{},true,{"id":1}]
You can also, create a POJO with list and use #JsonRawValue annotation. But if you can not have extra root object you need to implement custom serialiser for it. Example with POJO and custom serialiser:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
List<String> jsons = new ArrayList<>();
jsons.add("{}");
jsons.add("true");
jsons.add("{\"id\":1}");
RawJsons root = new RawJsons();
root.setJsons(jsons);
System.out.println(mapper.writeValueAsString(root));
}
}
#JsonSerialize(using = RawJsonSerializer.class)
class RawJsons {
private List<String> jsons;
public List<String> getJsons() {
return jsons;
}
public void setJsons(List<String> jsons) {
this.jsons = jsons;
}
}
class RawJsonSerializer extends JsonSerializer<RawJsons> {
#Override
public void serialize(RawJsons value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartArray();
if (value != null && value.getJsons() != null) {
for (String json : value.getJsons()) {
gen.writeRawValue(json);
}
}
gen.writeEndArray();
}
}
If you need to have SerializationFeature.INDENT_OUTPUT feature enabled for all items in array, you need to deserialise all inner objects and serialise them again.
See also:
How can I include raw JSON in an object using Jackson?
Handling raw JSON values using Jackson
the simple fact of having the character '[' we are marking that it is an array so what I recommend to put the list into a JSON array.
I would need a little more information to help you, since it doesn't make much sense to use a JSON String, since a JSON is composed of Key / Value, it is best to make a bean / object with the attribute.
Example:
class Object {
private String attribute = value;
}
{attribute: value}
I'm making an 3D engine using lwjgl.
I have tried to make a class to using a list of HashMap but the HashMap only accepts 2 variables so that does not work.
Part of my code for getting the JSON file
Gson().fromJson(string.toString(), BlockIndexFile.class);
the BlockIndexFile class
public class BlockIndexFile {
List<HashMap<String, String>> blocks = new ArrayList<HashMap<String, String>>();
public void setBlocks(List<HashMap<String, String>> blocks) {
this.blocks = blocks;
}
public List<HashMap<String, String>> getBlocks(){
return this.blocks;
}
}
and the json file
{
"blocks":
[
{
"name": "Foo",
"id": "foo",
"model": "cube1",
"texture": "foo"
}
]
}
I expected to be able to use a HashMap to get the id and then use that to get the other variables like the texture and model.
HashMap can contain more than 2 variables. See below example how you could use it:
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
BlockIndexFile blockIndexFile;
try (FileReader fileReader = new FileReader(jsonFile)) {
blockIndexFile = gson.fromJson(fileReader, BlockIndexFile.class);
}
HashMap<String, String> node0 = blockIndexFile.getBlocks().get(0);
System.out.println("id => " + node0.get("id"));
System.out.println("model => " + node0.get("id"));
System.out.println("texture => " + node0.get("id"));
}
}
Above code prints:
id =>foo
model =>foo
texture =>foo
Instead Map you can create POJO and code should be much easier and concise:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
BlockIndexFile blockIndexFile;
try (FileReader fileReader = new FileReader(jsonFile)) {
blockIndexFile = gson.fromJson(fileReader, BlockIndexFile.class);
}
Block node0 = blockIndexFile.getBlocks().get(0);
System.out.println(node0);
}
}
class BlockIndexFile {
private List<Block> blocks = new ArrayList<>();
// getters, setters
}
class Block {
private String id;
private String name;
private String model;
private String texture;
// getters, setters, toString
}
Above code prints:
Block{id='foo', name='Foo', model='cube1', texture='foo'}
Would it be possible if someone could help me parse this json result. I have retrieved the result as a string
{"query":{"latitude":39.9889,"longitude":-82.8118},"timestamp":1310252291.861,"address":{"geometry":{"coordinates":[-82.81168367358264,39.9887910986731],"type":"Point"},"properties":{"address":"284 Macdougal Ln","distance":"0.02","postcode":"43004","city":"Columbus","county":"Franklin","province":"OH","country":"US"},"type":"Feature"}}
Jackson. Simple and intuitive to use. For me the best available. Start out with Simple Data Binding, it will throw everything it finds in Maps and Lists.
Like this:
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> yourData = mapper.readValue(new File("yourdata.json"), Map.class);
That's all that's needed.
A good and quick introduction can be found here
And a full working example with your actual data:
import java.io.IOException;
import java.util.Map;
import org.codehaus.jackson.map.ObjectMapper;
public class Main {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Map<?,?> rootAsMap = mapper.readValue(
"{\"query\":{\"latitude\":39.9889,\"longitude\":-82.8118},\"timestamp\":1310252291.861,\"address\":{\"geometry\":{\"coordinates\":[-82.81168367358264,39.9887910986731],\"type\":\"Point\"},\"properties\":{\"address\":\"284 Macdougal Ln\",\"distance\":\"0.02\",\"postcode\":\"43004\",\"city\":\"Columbus\",\"county\":\"Franklin\",\"province\":\"OH\",\"country\":\"US\"},\"type\":\"Feature\"}}".getBytes(),
Map.class);
System.out.println(rootAsMap);
Map query = (Map) rootAsMap.get("query");
Map address = (Map) rootAsMap.get("address");
Map addressProperties = (Map) address.get("properties");
String county = (String) addressProperties.get("county");
System.out.println("County is " + county);
}
}
Now, this whole Map juggling also illustrates Bozho's point pretty well, using full binding (by creating a Java class that reflects the content of the JSON data) will work better in the end.
The two best options that I know of are:
Jackson
gson
Using them is a matter of calling one method of the mapper. But remember that since Java is statically-typed, you may have to create an object that has the required structure. (You don't have to, but it feels more natural)
From http://www.json.org, under the Java section:
http://www.json.org/java/index.html
http://json-lib.sourceforge.net/
http://code.google.com/p/json-simple/
http://code.google.com/p/jjson/
Pick your poison
With Jackson, following is the approach I'd take. Since the coordinates in the JSON come in two different formats -- sometimes an object, sometimes an array -- the solution is mildly complicated with necessary custom deserialization processing.
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.node.ArrayNode;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getVisibilityChecker().withFieldVisibility(Visibility.ANY));
mapper.registerModule(
new SimpleModule("CoordinatesDeserializer", Version.unknownVersion())
.addDeserializer(Coordinates.class, new CoordinatesDeserializer()));
Result result = mapper.readValue(new File("input.json"), Result.class);
System.out.println(mapper.writeValueAsString(result));
}
}
class CoordinatesDeserializer extends JsonDeserializer<Coordinates>
{
#Override
public Coordinates deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if (node.isObject())
{
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getVisibilityChecker().withFieldVisibility(Visibility.ANY));
return mapper.readValue(node, Coordinates.class);
}
// else it's an array
ArrayNode array = (ArrayNode) node;
Coordinates coordinates = new Coordinates();
coordinates.latitude = codec.treeToValue(array.get(0), BigDecimal.class);
coordinates.latitude = codec.treeToValue(array.get(1), BigDecimal.class);
return coordinates;
}
}
class Result
{
Coordinates query;
BigDecimal timestamp;
Address address;
}
class Coordinates
{
BigDecimal latitude;
BigDecimal longitude;
}
class Address
{
String type;
Geometry geometry;
AddressDetails properties;
}
class Geometry
{
String type;
Coordinates coordinates;
}
class AddressDetails
{
String address;
BigDecimal distance;
String postcode;
String city;
String county;
String province;
String country;
}