-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[json-core] Add JsonExtract helper to ease extracting values and numb…
…er types from raw Map<String,Object> Helps mostly when extracting values out of nested documents, and dealing with the various number types and type conversion
- Loading branch information
Showing
4 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
json-core/src/main/java/io/avaje/json/simple/DExtract.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package io.avaje.json.simple; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.regex.Pattern; | ||
|
||
final class DExtract implements JsonExtract { | ||
|
||
private static final Pattern PATH_PATTERN = Pattern.compile("\\."); | ||
|
||
private final Map<String, Object> map; | ||
|
||
DExtract(Map<String, Object> map) { | ||
this.map = map; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private Object find(String path, Map<String, Object> map) { | ||
final String[] paths = PATH_PATTERN.split(path, 2); | ||
final Object child = map.get(paths[0]); | ||
if (child == null || paths.length == 1) { | ||
return child; | ||
} | ||
if (child instanceof Map) { | ||
return find(paths[1], (Map<String, Object>) child); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public String extract(String path) { | ||
final var node = find(path, map); | ||
if (node == null) { | ||
throw new IllegalArgumentException("Node not present for " + path); | ||
} | ||
return node.toString(); | ||
} | ||
|
||
@Override | ||
public Optional<String> extractOrEmpty(String path) { | ||
final var name = find(path, map); | ||
return name == null ? Optional.empty() : Optional.of(name.toString()); | ||
} | ||
|
||
@Override | ||
public String extract(String path, String missingValue) { | ||
final var name = find(path, map); | ||
return name == null ? missingValue : name.toString(); | ||
} | ||
|
||
@Override | ||
public int extract(String path, int missingValue) { | ||
final var node = find(path, map); | ||
return !(node instanceof Number) | ||
? missingValue | ||
: ((Number) node).intValue(); | ||
} | ||
|
||
@Override | ||
public long extract(String path, long missingValue) { | ||
final var node = find(path, map); | ||
return !(node instanceof Number) | ||
? missingValue | ||
: ((Number) node).longValue(); | ||
} | ||
|
||
@Override | ||
public double extract(String path, double missingValue) { | ||
final var node = find(path, map); | ||
return !(node instanceof Number) | ||
? missingValue | ||
: ((Number) node).doubleValue(); | ||
} | ||
|
||
@Override | ||
public boolean extract(String path, boolean missingValue) { | ||
final var node = find(path, map); | ||
return !(node instanceof Boolean) | ||
? missingValue | ||
: (Boolean) node; | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
json-core/src/main/java/io/avaje/json/simple/JsonExtract.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package io.avaje.json.simple; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
/** | ||
* A helper to extract values from a Map. | ||
* <p> | ||
* The <em>path</em> can be simple like {@code "name"} or a nested path using | ||
* dot notation like {@code "address.city"}. | ||
* <p> | ||
* For extracting numbers there are methods for int, long and double that will | ||
* return the intValue(), longValue() and doubleValue() respectively. | ||
* <p> | ||
* <pre>{@code | ||
* | ||
* String json = "{\"name\":\"Rob\",\"score\":4.5,\"whenActive\":\"2025-10-20\",\"address\":{\"street\":\"Pall Mall\"}}"; | ||
* Map<String, Object> mapFromJson = simpleMapper.fromJsonObject(json); | ||
* | ||
* JsonExtract jsonExtract = simpleMapper.extract(mapFromJson); | ||
* | ||
* String name = jsonExtract.extract("name"); | ||
* double score = jsonExtract.extract("score", -1D); | ||
* String street = jsonExtract.extract("address.street"); | ||
* | ||
* LocalDate activeDate = jsonExtract.extractOrEmpty("whenActive") | ||
* .map(LocalDate::parse) | ||
* .orElseThrow(); | ||
* | ||
* }</pre> | ||
* | ||
*/ | ||
public interface JsonExtract { | ||
|
||
/** | ||
* Return a JsonExtract for the given Map of values. | ||
*/ | ||
static JsonExtract of(Map<String, Object> map) { | ||
return new DExtract(map); | ||
} | ||
|
||
/** | ||
* Extract the text from the node at the given path. | ||
* | ||
* @throws IllegalArgumentException When the given path is missing. | ||
*/ | ||
String extract(String path); | ||
|
||
/** | ||
* Extract the text value from the given path if present else empty. | ||
* | ||
* <pre>{@code | ||
* | ||
* LocalDate activeDate = jsonExtract.extractOrEmpty("whenActive") | ||
* .map(LocalDate::parse) | ||
* .orElseThrow(); | ||
* | ||
* }</pre> | ||
*/ | ||
Optional<String> extractOrEmpty(String path); | ||
|
||
/** | ||
* Extract the text value from the given path if present or the given default value. | ||
* | ||
* @param missingValue The value to use when the path is missing. | ||
*/ | ||
String extract(String path, String missingValue); | ||
|
||
/** | ||
* Extract the int from the given path if present or the given default value. | ||
* | ||
* @param missingValue The value to use when the path is missing. | ||
*/ | ||
int extract(String path, int missingValue); | ||
|
||
/** | ||
* Extract the long from the given path if present or the given default value. | ||
* | ||
* @param missingValue The value to use when the path is missing. | ||
*/ | ||
long extract(String path, long missingValue); | ||
|
||
/** | ||
* Extract the double from the given path if present or the given default value. | ||
* | ||
* @param missingValue The value to use when the path is missing. | ||
*/ | ||
double extract(String path, double missingValue); | ||
|
||
/** | ||
* Extract the boolean from the given path if present or the given default value. | ||
* | ||
* @param missingValue The value to use when the path is missing. | ||
*/ | ||
boolean extract(String path, boolean missingValue); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters