I want JsonPath to always return the data it parses as a string. However, the return type of JsonPath's read()
method varies depending on the the type of the data being parsed, because JsonPath always makes a guess at what the data type is and returns results of that type.
The problem is I have no way of knowing what kind of data is going to be returned as I only have access to a set of paths I need to feed into the read()
function. This means that I have to store the returned JsonPath data in an array of Objects and use a pretty ugly collection of if/else-if statements involving the instanceof
keyword to figure out what the data is (and then cast it to the appropriate type).
Does anyone know a way to force JsonPath to return a string? Or can another think of a better work around?
Here's what I'm doing right now (in Java):
ArrayList<Object> realData = JsonPath.read(dataDumpFile, current.getPath());
What I'd like to do:
ArrayList<String> realData = JsonPath.read(dataDumpFile, current.getPath());
In the docs I found this:
When using JsonPath in java its important to know what type you expect in your result. JsonPath will automatically try to cast the result to the type expected by the invoker.
//Will throw an java.lang.ClassCastException List<String> list = JsonPath.parse(json).read("$.store.book[0].author") //Works fine String author = JsonPath.parse(json).read("$.store.book[0].author")
What does "the type expected by the invoker" mean?
In a typical use case of JsonPath, the library assumes we know what to expect from a path:
Is the path definite? A path is:
$..book[0]
for the first book.$..book[?(@.isbn)]
for all books with certain properties; the return type can be a list.What specific type should the result be? For example, you can expect return types Date
or List<Date>
instead of Object
or List<Object>
.
In your case, all of the above doesn't matter because you don't know the type beforehand, you just need the results as JSON strings.
The default parser in JsonPath is too smart and will convert everything to nice LinkedHashMap objects; but if you use JacksonJsonNodeJsonProvider, you'll get results as JsonNode objects, and you're one toString()
call away from JSON string results.
Even better, you can use the JsonPath option ALWAYS_RETURN_LIST
, which means you don't need to worry about whether your path is definite or indefinite (whether your result is a single object or a list).
// Example from https://github.com/jayway/JsonPath#path-examples
final String json = "{\"store\": {\"book\": [{\"category\": \"reference\",\"author\": \"Nigel Rees\",\"title\": \"Sayings of the Century\",\"price\": 8.95},{\"category\": \"fiction\",\"author\": \"Evelyn Waugh\",\"title\": \"Sword of Honour\",\"price\": 12.99},{\"category\": \"fiction\",\"author\": \"Herman Melville\",\"title\": \"Moby Dick\",\"isbn\": \"0-553-21311-3\",\"price\": 8.99},{\"category\": \"fiction\",\"author\": \"J. R. R. Tolkien\",\"title\": \"The Lord of the Rings\",\"isbn\": \"0-395-19395-8\",\"price\": 22.99}],\"bicycle\": {\"color\": \"red\",\"price\": 19.95}},\"expensive\": 10}";
Configuration conf = Configuration.builder().jsonProvider(new JacksonJsonNodeJsonProvider())
.options(Option.ALWAYS_RETURN_LIST, Option.SUPPRESS_EXCEPTIONS).build();
ArrayNode node = JsonPath.using(conf).parse(json).read("$.store.book[*]"); // indefinite
for (Object o : node) {
System.out.println(o.toString());
}
// {"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}
// {"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}
// {"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}
// {"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}
node = JsonPath.using(conf).parse(json).read("$.store.book[0].author"); // definite
for (Object o : node) {
System.out.println(o.toString());
}
// "Nigel Rees"
Let assume this is your JSON:
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
Instead of parsing the whole JSON you can query that and get only those string values (as paths), then you can easily loop through them and get what you want:
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST).build();
List<String> authorPathList = using(conf).parse(json).read("$..author");
This will return the list of authors paths:
["$['store']['book'][0]['author']",
"$['store']['book'][1]['author']",
"$['store']['book'][2]['author']",
"$['store']['book'][3]['author']"]
Now you can loop through them and get your value:
for (string authorPath : authorPathList) {
String author = JsonPath.parse(json).read(authorPath);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With