JsonLoader.java
package org.wikimedia.eventutilities.core.json;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import org.wikimedia.eventutilities.core.util.ResourceLoader;
import org.wikimedia.eventutilities.core.util.ResourceLoadingException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
/**
* Uses a {@link ResourceLoader} to load content at URIs and parse it into YAML or JSON.
*
* If the data fetched from a URI starts with a { or [
* character, it will be assumed to be JSON and JsonParser will be used.
* Otherwise YAMLParser will be used. JSON data can contain certain unicode
* characters that YAML cannot, so it is best to use JsonParser when we can.
*/
public class JsonLoader {
final YAMLFactory yamlFactory = new YAMLFactory();
final JsonFactory jsonFactory = new JsonFactory();
// Make sure to reuse, expensive to create.
// This should be thread safe.
final ObjectMapper objectMapper = new ObjectMapper();
final ResourceLoader resourceLoader;
public JsonLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* Given a schemaURI, this will request the JSON or YAML content at that URI and
* parse it into a JsonNode. $refs will be resolved.
* The compiled schema will be cached by schemaURI, and only looked up once per schemaURI.
*
* @return the jsonschema at schemaURI.
*/
public JsonNode load(URI uri) throws JsonLoadingException {
JsonParser parser;
try {
parser = this.getParser(uri);
} catch (IOException | IllegalArgumentException | UncheckedIOException | ResourceLoadingException e) {
throw new JsonLoadingException("Failed reading JSON/YAML data from " + uri, e);
}
try {
return this.parse(parser);
} catch (IOException | IllegalArgumentException e) {
throw new JsonLoadingException("Failed loading JSON/YAML data from " + uri, e);
}
}
/**
* Parses the JSON or YAML string into a JsonNode.
*
* @param data JSON or YAML string to parse into a JsonNode.
*/
public JsonNode parse(String data) throws JsonLoadingException {
try {
return this.parse(this.getParser(data));
} catch (IOException | IllegalArgumentException e) {
throw new JsonLoadingException(
"Failed parsing JSON/YAML data from string '" + data + '"', e
);
}
}
/**
* Convenience method to reuse our ObjectMapper to serialize a JsonNode
* to a JSON String.
*/
public String asString(JsonNode jsonNode) throws JsonProcessingException {
return objectMapper.writeValueAsString(jsonNode);
}
/**
* Convenience method to reuse our ObjectMapper to convert a JsonNode to a Java Class.
* See: {@link ObjectMapper#convertValue(Object, Class)}
*/
public <T> T convertValue(JsonNode jsonNode, Class<T> t) {
return objectMapper.convertValue(jsonNode, t);
}
/**
* Returns the underlying {@link ResourceLoader}.
*/
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
/**
* Given a {@link JsonParser}, reads it in as a JsonNode.
*/
private JsonNode parse(JsonParser parser) throws IOException {
return objectMapper.readTree(parser);
}
/**
* Gets a {@link JsonParser} for String data.
* If the data starts with a { or [, this will be parsed as JSON,
* else it will be parsed as YAML.
*/
private JsonParser getParser(String data) throws IOException {
// If the first character is { or [, assume this is
// JSON data and use a JsonParser. Otherwise assume
// YAML and use a YAMLParser.
char firstChar = data.charAt(0);
if (firstChar == '{' || firstChar == '[') {
return jsonFactory.createParser(data);
} else {
return yamlFactory.createParser(data);
}
}
/**
* Gets either a YAMLParser or a JsonParser for the data at uri.
* If the data starts with a { or [, this will be parsed as JSON,
* else it will be parsed as YAML.
*/
private JsonParser getParser(URI uri) throws IOException, ResourceLoadingException {
return this.getParser(new String(resourceLoader.load(uri), UTF_8));
}
public String toString() {
return "JsonLoader(" + getResourceLoader() + ")";
}
}