I've downloaded a large amount of historic crypto market data via an API. It is formatted like this:
[
[1601510400000,"4.15540000","4.16450000","4.15010000","4.15030000","4483.01000000",1601510459999,"18646.50051400",50,"2943.27000000","12241.83706500","0"],
...
[1609490340000,"4.94020000","4.95970000","4.93880000","4.94950000","5307.62000000",1609490399999,"26280.03711000",98,"3751.46000000","18574.22402400","0"]
]
I take that to be an array of arrays, the inner one containing heterogeneous types (always the same types in the same order). As an intermediate step I've saved it to text files but I'd like to read it back and map it onto an array of objects of this type:
public class MinuteCandle {
private long openTime;
private double openValue;
private double highValue;
private double lowValue;
private double closeValue;
private double volume;
private long closeTime;
private double quoteAssetVolume;
private int numberOfTrades;
private double takerBuyBaseAssetVolume;
private double takerBuyQuoteAssetVolume;
private double someGarbageData;
//...
}
I'm using the Spring Framework and the jackson library for json mapping. Is it doable with that or should I manually parse the text somehow?
Use JsonFormat and annotate your class with it where you specify shape as an ARRAY:
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
class MinuteCandle
Also, consider to use BigDecimal instead of double if you want to store a price.
See also:
A realistic example where using BigDecimal for currency is strictly
better than using double
How to deserialise anonymous array of mixed types with Jackson
I would do this in two steps:
Read the JSON content into a list of List<Object> with Jackson.
Convert each List<Object> into a MinuteCandle object
and collect these objects into a list of MinuteCandles.
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("example.json");
List<List<Object>> lists = objectMapper.readValue(file, new TypeReference<List<List<Object>>>() {});
List<MinuteCandle> minuteCandles = new ArrayList<>();
for (List<Object> list : lists) {
minuteCandles.add(MinuteCandle.createFromList(list));
}
}
}
The conversion from List<Object> to MinuteCandle (step 2 from above)
could be achieved by adding a static method in your MinuteCandle class.
public static MinuteCandle createFromList(List<Object> list) {
MinuteCandle m = new MinuteCandle();
m.openTime = (Long) list.get(0);
m.openValue = Double.parseDouble((String) list.get(1));
m.highValue = Double.parseDouble((String) list.get(2));
m.lowValue = Double.parseDouble((String) list.get(3));
m.closeValue = Double.parseDouble((String) list.get(4));
m.volume = Double.parseDouble((String) list.get(5));
m.closeTime = (Long) list.get(6);
m.quoteAssetVolume = Double.parseDouble((String) list.get(7));
m.numberOfTrades = (Integer) list.get(8);
m.takerBuyBaseAssetVolume = Double.parseDouble((String) list.get(9));
m.takerBuyQuoteAssetVolume = Double.parseDouble((String) list.get(10));
m.someGarbageData = Double.parseDouble((String) list.get(11));
return m;
}
Assuming the text stored in the file is valid JSON, similar to the solution in How to Read JSON data from txt file in Java? one can use com.google.gson.Gson as follows :
import com.google.gson.Gson;
import java.io.FileReader;
import java.io.Reader;
public class Main {
public static void main(String[] args) throws Exception {
try (Reader reader = new FileReader("somefile.txt")) {
Gson gson = new Gson();
MinuteCandle[] features = gson.fromJson(reader, MinuteCandle[].class);
}
}
}
Related
I am trying to get the rates from https://api.ratesapi.io/api/latest into an ArrayList<Currency> of a custom Currency class:
public class Currency {
private String shortName;
private double rate;
...
}
The JSON looks like:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786,"IDR":17304.0,
"ILS":4.0309,"DKK":7.45,"INR":88.765,"CHF":1.0759,"MXN":26.615,
"CZK":26.202,"SGD":1.6236,"THB":36.832,"HRK":7.468,"MYR":4.9604,
"NOK":10.6538,"CNY":8.2325,"BGN":1.9558,"PHP":58.136,"SEK":10.3165,
"PLN":4.4073,"ZAR":20.7655,"CAD":1.5748,"ISK":160.2,"BRL":6.334,
"RON":4.836,"NZD":1.7828,"TRY":8.5853,"JPY":124.96,"RUB":86.9321,
"KRW":1404.99,"USD":1.1843,"HUF":346.23,"AUD":1.6492},"date":"2020-08-06"}
Using org.json I managed to get the data into a JSONObject:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
As far as I understand, the normal procedure is now to convert the JSONObject into a JSONArray. However trying:
JSONArray jsonArray = obj.getJSONArray("rates");
fails with the error message:
Exception in thread "main" org.json.JSONException: JSONObject["rates"]
is not a JSONArray.
How do I fix this error or is there another way to make an ArrayList out of the JSON?
I suspect that the problem are missing square brackets in the JSON string.
If you take a look at the JSON returned by the API, you get a JSON object:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786, ... },"date":"2020-08-06"}
You probably want to do something like this:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
JSONObject rates = obj.getJSONObject("rates");
final Iterator<String> keys = rates.keys();
while (keys.hasNext()) {
final String key = keys.next();
final Currency currency = new Currency(key, rates.getDouble(key));
// do something with the Currency
}
The object "rates" is not a JSONArray, is a JSONObject.
So you have to do obj.getJSONObject(rates");then iterate on the fields of the JSONObject using map methods (for examply using keySet() )
A working solution using Jackson library and Lombok may be as follows:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.*;
import java.util.*;
import java.util.stream.Collectors;
public class CcyApiParser {
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public static class Currency {
private String shortName;
private double rate;
}
#Getter
#Setter
public static class RatesApiResponse {
private String base;
private Map<String, Double> rates;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule()); // to parse date
URL apiUrl = new URL("https://api.ratesapi.io/api/latest");
// read proper api response
RatesApiResponse rates = mapper.readValue(apiUrl, RatesApiResponse.class);
// convert inner rates into list of Currency objects
List<Currency> ccys = rates.getRates().entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
ccys.forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
}
}
Output
GBP=0.90033
HKD=9.1786
IDR=17304.0
ILS=4.0309
... etc.
Update
It is also possible to customize deserialization of RatesApiResponse and move mapping of "rates" into this class to convert immediately into list of currencies.
#Getter
#Setter
public static class RatesApiResponse {
private String base;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private List<Currency> ccys;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
// no getter for rates
// this customized setter for the map of rates converts into a list
#JsonProperty("rates")
public void setRates(Map<String, Double> rates) {
ccys = rates.entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
}
// Updates in the test method
RatesApiResponse rates = mapper.readValue(src, RatesApiResponse.class);
rates.getCcys().forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
You can use ObjectMapper class to convert json from some URL to some kind of object. In this case (if json structure is always the same) it can be Map<String, Object>.
ObjectMapper mapper = new ObjectMapper();
URL url = new URL("https://api.ratesapi.io/api/latest");
Map<String, Object> map = mapper.readValue(url, Map.class);
System.out.println(map);
// {base=EUR, rates={GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}, date=2020-08-07}
Then you can get inner rates map, and (if it is needed) convert it to list using java stream api:
Map<String, Double> rates = (Map<String, Double>) map.get("rates");
System.out.println(rates); // {GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}
Convert Map<String, Object> to ArrayList<Currency>:
ArrayList<Currency> list = rates.entrySet().stream()
.map(entry -> new Currency(entry.getKey(), entry.getValue()))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(list); // [GBP=0.90373, HKD=9.1585, ... , AUD=1.6403]
Note: add a constructor with two fields shortName and rate;
Note: override the toString method as follows: shortName + "=" + rate;
Maven dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.2</version>
</dependency>
See also: «Formatting Json Response into an Array Java».
Exception in thread "main" org.json.JSONException: JSONObject["rates"]
is not a JSONArray.
You got this error because rates is not in the form of an array. It is simply an element like base and date but looks like an array. Get it from the JSON string like you get base and date from it and then process it to create the required List<Currency>.
Given below is the working code with the explanation added as comments in the code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
class Currency {
private String shortName;
private double rate;
public Currency(String shortName, double rate) {
this.shortName = shortName;
this.rate = rate;
}
#Override
public String toString() {
return shortName + ":" + rate;
}
}
public class Main {
public static JSONObject getJSON(String url) throws IOException, JSONException {
// Create a URLConnection for the given URL
URLConnection connection = new URL(url).openConnection();
// Add header to avoid 403 Forbidden HTTP status code
connection.addRequestProperty("User-Agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:79.0) Gecko/20100101 Firefox/79.0" + "");
StringBuilder jsonStr = new StringBuilder();
// Get InputStream from connection and read the response
try (InputStream is = connection.getInputStream();) {
Reader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
int ch;
while ((ch = reader.read()) != -1) {
jsonStr.append((char) ch);
}
}
return new JSONObject(jsonStr.toString());
}
public static void main(String[] args) throws IOException, JSONException {
JSONObject jsonObj = getJSON("https://api.ratesapi.io/api/latest");
// Get rates from jsonObj
String rates = jsonObj.get("rates").toString();
// Remove {, }, and " from the string
String[] keyValArr = rates.replaceAll("[\\{\\\"}]", "").split(",");
// List object to hold Currency objects
List<Currency> list = new ArrayList<>();
for (String keyVal : keyValArr) {
// Split each key:value string on ':'
String[] curRate = keyVal.split(":");
// Add Currency object to List
list.add(new Currency(curRate[0], Double.parseDouble(curRate[1])));
}
// Display list
list.forEach(System.out::println);
}
}
Output:
CHF:1.0804
HRK:7.4595
MXN:26.5127
...
...
...
NZD:1.7786
BRL:6.3274
I have some json and it's fairly complex -- (a bit too complex and open-ended to model using something like gson), and I need to extract string values from certain nodes into a list of strings.
The following code works, but due to the way my json works -- it's grabbing lots of extra stuff that I don't want (note: I don't own the json schema)
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
List<JsonNode> keys = node.findValues("key") ;
for(JsonNode key: keys){
System.out.println(key.toString());
}
The contents of Json is fairly complex (Jira filter export) which looks like this:
{
"issues": [
{
"key":"MIN-123",
...
"fields":{
"key":"A_Elric"
}
}
]
}
Assertions:
I always want to extract issues[x].key and not any of the subkeys. I would prefer to extract this into a list, but any normal data structure is fine. I'm already using Jackson -- but gson is also an option if there's a sane way of doing so.
Thanks for the assist!
JsonPath is xpath for json, and it has a Java implementation.
Here is a working example to get issue keys without subkeys:
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class JsonPathTest {
public static String ROOT_ARRAY = "issues";
public static String KEY = "key";
// get all KEYs right under ROOT array
public static String jsonPath = String.format("$.%s[*].%s", ROOT_ARRAY, KEY);
public static void main(String[] args) {
try {
String jsonStr = new String(Files.readAllBytes(Paths.get("c:/temp/xx.json")));
Object jsonObj = Configuration.defaultConfiguration().jsonProvider().parse(jsonStr);
List<String> keys = JsonPath.parse(jsonObj).read(jsonPath);
System.out.println(keys);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ExportFilter{
private static final String KEY = "key";
private List<Map<String,Object>> issues = new ArrayList<>();
//getters and setters
#JsonIgnore
public List<String> getKeys(){
return issues.stream()
.map(issue-> issue.get(KEY))
.filter(Objects::nonNull)
.map(Objects::toString)
.collect(toList());
}
}
Example usage:
ObjectMapper objectMapper = new ObjectMapper();
List<String> keys = objectMapper.readValue( .., ExportFilter.class).getKeys();
I'm trying to convert following string to an object,
{
"base":"TRY",
"date":"2017-01-06",
"rates":{
"EUR":0.37679,
"USD":0.51059,
"BRL":0.88513,
"CAD":0.36651,
...
}
}
I know, if I create an object containing all rates as Double attribute, I will be able to convert that object into an object. But I want rates to be in an array like below. How can I create convertedJsonObjectArrayList. Thanks in advance.
List<Rate> rates = convertedJsonObjectArrayList;
class Rate{
String name; //EUR
Double value; //0.37679
}
This assumes that you're guaranteed to have content within the JSON object associated to the name "rates" that will be parseable as a List.
Define a custom deserializer to consume the name-value tokens as pairs
class RatesJsonObjectToArray extends JsonDeserializer<List<Rate>> {
#Override
public List<Rate> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
List<Rate> rates = new ArrayList<>();
// consume two tokens at a time, a name and a value
for (;;) {
String name = p.nextFieldName();
if (name == null) {
break; // no more input
}
JsonToken doubleValueToken = p.nextValue();
if (doubleValueToken != JsonToken.VALUE_NUMBER_FLOAT) { // there's also VALUE_NUMBER_INT for more flexibility
throw new JsonParseException("Expected a numeric value.");
}
double value = p.getDoubleValue();
rates.add(new Rate(name, value));
}
return rates;
}
}
Then annotate your rates field/setter with this JsonDeserializer
#JsonProperty
#JsonDeserialize(using = RatesJsonObjectToArray.class)
private List<Rate> rates;
You can get fancy and write a custom deserializer.
However, if you don't mind being a bit more quick-and-dirty, you can just deserialize to a Map, and convert to your preferred structure explicitly:
String ratesAsJson = "{ \"EUR\" : 0.2, \"USD\":1.0 }";
ObjectMapper objectMapper = new ObjectMapper();
TypeReference<Map<String, Double>> typeRef = new TypeReference<Map<String, Double>>() {};
Map<String, Double> map = objectMapper.readValue(ratesAsJson, typeRef);
List<Rate> list = map.entrySet().stream()
.map(entry -> new Rate(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
(I've assumed a 2-arg constructor on the Rate class).
If you write a custom deserializer, it will have very similar code to the above within it.
I've used a stream and map(). You could use a for loop, but hey, it's 2017!
If you want to convert the currency object(json) into an array(java) You can not use serialization(gson/jaxb) you would need to parse (JsonParser) json and make the conversion yourself
Here is my solution;
ObjectMapper mapper = new ObjectMapper();
CurrencyServiceResponse currencyResp =
mapper.readValue(jsonStr,new TypeReference<CurrencyServiceResponse>(){});
currencyResp.setRates(new ArrayList<>());
for(Map.Entry<String,Double> entry: currencyResp.getRateMap().entrySet()){
currencyResp.getRates().add(
new CurrencyServiceResponse.Rate(entry.getKey(),entry.getValue()));
}
And class for conversion
public class CurrencyServiceResponse {
#JsonProperty("base")
private String base;
#JsonProperty("date")
private Date date;
#JsonProperty("rates")
private Map<String,Double> rateMap;
#JsonIgnore
private List<Rate> rates;
//getters&setters
public static class Rate{
private String name;
private Double value;
public Rate(String name, Double value) {
this.name = name;
this.value = value;
}
//getters&setters
}
}
You can use GSON library. It's the Google JSON library.
JsonParser jp = new JsonParser();
JsonElement root = jp.parse(YOUR_DATA);
JsonObject rootobj = root.getAsJsonObject();
And in your case to access to the EUR rate you can use :
Double rate_eur = rootobj.get("rates").getAsJsonObject().get("EUR").getAsDouble();
Don't forget to import classes :
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
And dependencies :
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>RELEASE</version>
</dependency>
for maven for example.
For example my JSON text is coming like this.
"pages":{"42010":{"pageid":42010,"ns":0,"title":"Queen (band)"}}
Because everytime my json text is coming with different number which is inside pages tag.
How do i convert this to Java equivalent class?
Currently my generated java class is something like this.
#Generated("org.jsonschema2pojo")
public class Pages {
#SerializedName("42010")
#Expose
private _42010 _42010;
}
That _42010 class contains the inner fields like "pageid":42010,"ns":0,"title":"Queen (band)", since i am getting everytime new number inside pages, its not working. its working only for the specific json text.
You can use a custom deserialiser that ignored the changing number. For example:
package jacksonTest;
import java.io.IOException;
import java.lang.reflect.Type;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class CustomDeserialiser {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String json = "{\"42010\":{\"pageid\":42010,\"ns\":0,\"title\":\"Queen (band)\"}}";
String json2 = "{\"12345\":{\"pageid\":12345,\"ns\":0,\"title\":\"Queen (band)\"}}";
Gson g = new GsonBuilder().registerTypeAdapter(Pages.class, new PagesDeserialiser()).create();
Pages fromJson = g.fromJson(json, Pages.class);
System.out.println(fromJson);
fromJson = g.fromJson(json2, Pages.class);
System.out.println(fromJson);
}
public static class PagesDeserialiser implements JsonDeserializer<Pages> {
#Override
public Pages deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws com.google.gson.JsonParseException {
JsonObject object = json.getAsJsonObject();
Pages p = new Pages();
object.entrySet().forEach( e -> {
JsonObject tmp = e.getValue().getAsJsonObject();
if(tmp.get("pageid") != null) {
// right object
p._42010 = new _42010();
p._42010.ns = tmp.get("ns").getAsInt();
p._42010.pageid = tmp.get("pageid").getAsInt();
p._42010.title = tmp.get("title").getAsString();
}
});
return p;
}
}
public static class Pages {
_42010 _42010;
#Override
public String toString() {
return _42010.toString();
}
}
public static class _42010 {
int pageid;
int ns;
String title;
#Override
public String toString() {
return title + " " + pageid + " " + ns;
}
}
}
The deserialiser for type pages simply checks the entries to find the one that contains a pageId and then populates the class.
Running my test gives you:
Queen (band) 42010 0
Queen (band) 12345 0
I am assuming that you are using Gson as your json library.
Regards,
Artur
Why do not use an JSON library like jackson or org.json?
Make your json correct like
{
"pages":{
"42010":{
"pageid":42010,
"ns":0,
"title":"Queen (band)"
}
}
}
And you will be able to use it like :
JSONObject jsonObjet = new JSONObject(yourJson);
jsonObjet.get("pages");
Ideally it should be using Map.
This helps in forming the values as Map<Integer, Pojo>.
Lets say
public class Pojo{
private int pageid;
private String title;
private int ns;
// getter and setter
}
This suffices the requirement of holding the random digits, generated at runtime,
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;
}