org.infinispan.commons.dataconversion.internal.Json Maven / Gradle / Ivy
package org.infinispan.commons.dataconversion.internal;
/*
* Copyright (C) 2011 Miami-Dade County.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: this file incorporates source code from 3d party entities. Such code
* is copyrighted by those entities as indicated below.
*/
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;
import org.infinispan.commons.dataconversion.MediaType;
/**
*
* Represents a JSON (JavaScript Object Notation) entity. For more information about JSON, please see
* http://www.json.org.
*
*
*
* A JSON entity can be one of several things: an object (set of name/Json entity pairs), an array (a list of other JSON
* entities), a string, a number, a boolean or null. All of those are represented as Json
instances. Each
* of the different types of entities supports a different set of operations. However, this class unifies all operations
* into a single interface so in Java one is always dealing with a single object type: this class. The approach
* effectively amounts to dynamic typing where using an unsupported operation won't be detected at compile time, but
* will throw a runtime {@link UnsupportedOperationException}. It simplifies working with JSON structures considerably
* and it leads to shorter at cleaner Java code. It makes much easier to work with JSON structure without the need to
* convert to "proper" Java representation in the form of POJOs and the like. When traversing a JSON, there's no need to
* type-cast at each step because there's only one type: Json
.
*
*
*
* One can examine the concrete type of a Json
with one of the isXXX
methods:
* {@link #isObject()}, {@link #isArray()},{@link #isNumber()},{@link #isBoolean()},{@link #isString()},
* {@link #isNull()}.
*
*
*
* The underlying representation of a given Json
instance can be obtained by calling the generic
* {@link #getValue()} method or one of the asXXX
methods such as {@link #asBoolean()} or
* {@link #asString()} etc. JSON objects are represented as Java {@link Map}s while JSON arrays are represented as Java
* {@link List}s. Because those are mutable aggregate structures, there are two versions of the corresponding
* asXXX
methods: {@link #asMap()} which performs a deep copy of the underlying map, unwrapping every
* nested Json entity to its Java representation and {@link #asJsonMap()} which simply return the map reference.
* Similarly there are {@link #asList()} and {@link #asJsonList()}.
*
*
* Constructing and Modifying JSON Structures
*
*
* There are several static factory methods in this class that allow you to create new
* Json
instances:
*
*
*
* {@link #read(String)}
* Parse a JSON string and return the resulting Json
instance. The syntax
* recognized is as defined in http://www.json.org.
*
*
* {@link #make(Object)}
* Creates a Json instance based on the concrete type of the parameter. The types
* recognized are null, numbers, primitives, String, Map, Collection, Java arrays
* and Json
itself.
*
* {@link #nil()}
* Return a Json
instance representing JSON null
.
*
* {@link #object()}
* Create and return an empty JSON object.
*
* {@link #object(Object...)}
* Create and return a JSON object populated with the key/value pairs
* passed as an argument sequence. Each even parameter becomes a key (via
* toString
) and each odd parameter is converted to a Json
* value.
*
* {@link #array()}
* Create and return an empty JSON array.
*
* {@link #array(Object...)}
* Create and return a JSON array from the list of arguments.
*
*
*
*
* To customize how Json elements are represented and to provide your own version of the
* {@link #make(Object)} method, you create an implementation of the {@link Factory} interface
* and configure it either globally with the {@link #setGlobalFactory(Factory)} method or
* on a per-thread basis with the {@link #attachFactory(Factory)}/{@link #detachFactory()}
* methods.
*
*
*
* If a Json
instance is an object, you can set its properties by
* calling the {@link #set(String, Object)} method which will add a new property or replace an existing one.
* Adding elements to an array Json
is done with the {@link #add(Object)} method.
* Removing elements by their index (or key) is done with the {@link #delAt(int)} (or
* {@link #delAt(String)}) method. You can also remove an element from an array without
* knowing its index with the {@link #remove(Object)} method. All these methods return the
* Json
instance being manipulated so that method calls can be chained.
* If you want to remove an element from an object or array and return the removed element
* as a result of the operation, call {@link #atDel(int)} or {@link #atDel(String)} instead.
*
*
*
* If you want to add properties to an object in bulk or append a sequence of elements to array,
* use the {@link #with(Json, Json...opts)} method. When used on an object, this method expects another
* object as its argument and it will copy all properties of that argument into itself. Similarly,
* when called on array, the method expects another array and it will append all elements of its
* argument to itself.
*
*
*
* To make a clone of a Json object, use the {@link #dup()} method. This method will create a new
* object even for the immutable primitive Json types. Objects and arrays are cloned
* (i.e. duplicated) recursively.
*
*
* Navigating JSON Structures
*
*
* The {@link #at(int)} method returns the array element at the specified index and the
* {@link #at(String)} method does the same for a property of an object instance. You can
* use the {@link #at(String, Object)} version to create an object property with a default
* value if it doesn't exist already.
*
*
*
* To test just whether a Json object has a given property, use the {@link #has(String)} method. To test
* whether a given object property or an array elements is equal to a particular value, use the
* {@link #is(String, Object)} and {@link #is(int, Object)} methods respectively. Those methods return
* true if the given named property (or indexed element) is equal to the passed in Object as the second
* parameter. They return false if an object doesn't have the specified property or an index array is out
* of bounds. For example is(name, value) is equivalent to 'has(name) && at(name).equals(make(value))'.
*
*
*
* To help in navigating JSON structures, instances of this class contain a reference to the
* enclosing JSON entity (object or array) if any. The enclosing entity can be accessed
* with {@link #up()} method.
*
*
*
* The combination of method chaining when modifying Json
instances and
* the ability to navigate "inside" a structure and then go back to the enclosing
* element lets one accomplish a lot in a single Java statement, without the need
* of intermediary variables. Here for example how the following JSON structure can
* be created in one statement using chained calls:
*
*
*
* {"menu": {
* "id": "file",
* "value": "File",
* "popup": {
* "menuitem": [
* {"value": "New", "onclick": "CreateNewDoc()"},
* {"value": "Open", "onclick": "OpenDoc()"},
* {"value": "Close", "onclick": "CloseDoc()"}
* ]
* }
* "position": 0
* }}
*
*
*
* import mjson.Json;
* import static mjson.Json.*;
* ...
* Json j = object()
* .at("menu", object())
* .set("id", "file")
* .set("value", "File")
* .at("popup", object())
* .at("menuitem", array())
* .add(object("value", "New", "onclick", "CreateNewDoc()"))
* .add(object("value", "Open", "onclick", "OpenDoc()"))
* .add(object("value", "Close", "onclick", "CloseDoc()"))
* .up()
* .up()
* .set("position", 0)
* .up();
* ...
*
*
*
* If there's no danger of naming conflicts, a static import of the factory methods (
* import static json.Json.*;
) would reduce typing even further and make the code more
* readable.
*
*
* Converting to String
*
*
* To get a compact string representation, simply use the {@link #toString()} method. If you
* want to wrap it in a JavaScript callback (for JSON with padding), use the {@link #pad(String)}
* method.
*
*
* Validating with JSON Schema
*
*
* Since version 1.3, mJson supports JSON Schema, draft 4. A schema is represented by the internal
* class {@link Json.Schema}. To perform a validation, you have a instantiate a Json.Schema
* using the factory method {@link Json.Schema} and then call its validate
method
* on a JSON instance:
*
*
*
* import mjson.Json;
* import static mjson.Json.*;
* ...
* Json inputJson = Json.read(inputString);
* Json schema = Json.schema(new URI("http://mycompany.com/schemas/model"));
* Json errors = schema.validate(inputJson);
* for (Json error : errors.asJsonList())
* System.out.println("Validation error " + err);
*
*
* Infinispan changes on top of 1.4.2:
*
*
* - Added support for pretty printing {@link Json#toPrettyString()}
* - Added support for {@link RawJson} as a specialized {@link StringJson}
* - Usage of {@link LinkedHashMap} internally for {@link ObjectJson} for predictable iteration
* - Support for {@link Class}, {@link Properties}, {@link Enum} for {@link DefaultFactory#make(Object)}
* - Support from internal Infinispan classes for {@link DefaultFactory#make(Object)}: {@link MediaType}, {@link JsonSerialization}, {@link ConfigurationInfo}
* - Support for replacing objects
*
*
* @author Borislav Iordanov
* @version 1.4.2
*/
public class Json implements java.io.Serializable {
private static final long serialVersionUID = 1L;
/**
*
* This interface defines how Json
instances are constructed. There is a default implementation for each
* kind of Json
value, but you can provide your own implementation. For example, you might want a
* different representation of an object than a regular HashMap
. Or you might want string comparison to
* be case insensitive.
*
*
*
* In addition, the {@link #make(Object)} method allows you plug-in your own mapping of arbitrary Java objects to
* Json
instances. You might want to implement a Java Beans to JSON mapping or any other JSON
* serialization that makes sense in your project.
*
*
*
* To avoid implementing all methods in that interface, you can extend the {@link DefaultFactory} default
* implementation and simply overwrite the ones you're interested in.
*
*
*
* The factory implementation used by the Json
classes is specified simply by calling the
* {@link #setGlobalFactory(Factory)} method. The factory is a static, global variable by default. If you need
* different factories in different areas of a single application, you may attach them to different threads of
* execution using the {@link #attachFactory(Factory)}. Recall a separate copy of static variables is made per
* ClassLoader, so for example in a web application context, that global factory can be different for each web
* application (as Java web servers usually use a separate class loader per application). Thread-local factories are
* really a provision for special cases.
*
*
* @author Borislav Iordanov
*/
public interface Factory {
/**
* Construct and return an object representing JSON null
. Implementations are free to cache a return
* the same instance. The resulting value must return
* true
from isNull()
and null
from
* getValue()
.
*
* @return The representation of a JSON null
value.
*/
Json nil();
/**
* Construct and return a JSON boolean. The resulting value must return
* true
from isBoolean()
and the passed
* in parameter from getValue()
.
*
* @param value The boolean value.
* @return A JSON with isBoolean() == true
. Implementations are free to cache and return the same
* instance for true and false.
*/
Json bool(boolean value);
/**
* Construct and return a JSON string. The resulting value must return
* true
from isString()
and the passed
* in parameter from getValue()
.
*
* @param value The string to wrap as a JSON value.
* @return A JSON element with the given string as a value.
*/
Json string(String value);
Json raw(String value);
/**
* Construct and return a JSON number. The resulting value must return
* true
from isNumber()
and the passed
* in parameter from getValue()
.
*
* @param value The numeric value.
* @return Json instance representing that value.
*/
Json number(Number value);
/**
* Construct and return a JSON object. The resulting value must return
* true
from isObject()
and an implementation
* of java.util.Map
from getValue()
.
*
* @return An empty JSON object.
*/
Json object();
/**
* Construct and return a JSON object. The resulting value must return
* true
from isArray()
and an implementation
* of java.util.List
from getValue()
.
*
* @return An empty JSON array.
*/
Json array();
/**
* Construct and return a JSON object. The resulting value can be of any JSON type. The method is responsible for
* examining the type of its argument and performing an appropriate mapping to a Json
instance.
*
* @param anything An arbitray Java object from which to construct a Json
element.
* @return The newly constructed Json
instance.
*/
Json make(Object anything);
}
public interface Function {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
/**
*
* Represents JSON schema - a specific data format that a JSON entity must follow. The idea of a JSON schema is very
* similar to XML. Its main purpose is validating input.
*
*
*
* More information about the various JSON schema specifications can be found at http://json-schema.org. JSON Schema
* is an IETF draft (v4 currently) and our implementation follows this set of specifications. A JSON schema is
* specified as a JSON object that contains keywords defined by the specification. Here are a few introductory
* materials:
*
* - http://jsonary.com/documentation/json-schema/ -
* a very well-written tutorial covering the whole standard
* - http://spacetelescope.github.io/understanding-json-schema/ -
* online book, tutorial (Python/Ruby based)
*
*
*
* @author Borislav Iordanov
*/
public interface Schema {
/**
*
* Validate a JSON document according to this schema. The validations attempts to proceed even in the face of
* errors. The return value is always a Json.object
containing the boolean property ok
.
* When ok
is true
, the return object contains nothing else. When it is
* false
, the return object contains a property errors
which is an array of error
* messages for all detected schema violations.
*
*
* @param document The input document.
* @return {"ok":true}
or {"ok":false, errors:["msg1", "msg2", ...]}
*/
Json validate(Json document);
/**
* Return the JSON representation of the schema.
*/
Json toJson();
/**
* Possible options are: ignoreDefaults:true|false
.
*
* @return A newly created Json
conforming to this schema.
*/
//Json generate(Json options);
}
static String fetchContent(URL url) {
java.io.Reader reader = null;
try {
reader = new java.io.InputStreamReader((java.io.InputStream) url.getContent(), StandardCharsets.UTF_8);
StringBuilder content = new StringBuilder();
char[] buf = new char[1024];
for (int n = reader.read(buf); n > -1; n = reader.read(buf))
content.append(buf, 0, n);
return content.toString();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
if (reader != null) try {
reader.close();
} catch (Throwable t) {
}
}
}
static Json resolvePointer(String pointerRepresentation, Json top) {
String[] parts = pointerRepresentation.split("/");
Json result = top;
for (String p : parts) {
// TODO: unescaping and decoding
if (p.isEmpty())
continue;
p = p.replace("~1", "/").replace("~0", "~");
if (result.isArray())
result = result.at(Integer.parseInt(p));
else if (result.isObject())
result = result.at(p);
else
throw new RuntimeException("Can't resolve pointer " + pointerRepresentation +
" on document " + top.toString(200));
}
return result;
}
static URI makeAbsolute(URI base, String ref) throws Exception {
URI refuri;
if (base != null && base.getAuthority() != null && !new URI(ref).isAbsolute()) {
StringBuilder sb = new StringBuilder();
if (base.getScheme() != null)
sb.append(base.getScheme()).append("://");
sb.append(base.getAuthority());
if (!ref.startsWith("/")) {
if (ref.startsWith("#"))
sb.append(base.getPath());
else {
int slashIdx = base.getPath().lastIndexOf('/');
sb.append(slashIdx == -1 ? base.getPath() : base.getPath().substring(0, slashIdx)).append("/");
}
}
refuri = new URI(sb.append(ref).toString());
} else if (base != null)
refuri = base.resolve(ref);
else
refuri = new URI(ref);
return refuri;
}
static Json resolveRef(URI base,
Json refdoc,
URI refuri,
Map resolved,
Map expanded,
Function uriResolver) throws Exception {
if (refuri.isAbsolute() &&
(base == null || !base.isAbsolute() ||
!base.getScheme().equals(refuri.getScheme()) ||
!Objects.equals(base.getHost(), refuri.getHost()) ||
base.getPort() != refuri.getPort() ||
!base.getPath().equals(refuri.getPath()))) {
URI docuri = null;
refuri = refuri.normalize();
if (refuri.getHost() == null)
docuri = new URI(refuri.getScheme() + ":" + refuri.getPath());
else
docuri = new URI(refuri.getScheme() + "://" + refuri.getHost() +
((refuri.getPort() > -1) ? ":" + refuri.getPort() : "") +
refuri.getPath());
refdoc = uriResolver.apply(docuri);
refdoc = expandReferences(refdoc, refdoc, docuri, resolved, expanded, uriResolver);
}
if (refuri.getFragment() == null)
return refdoc;
else
return resolvePointer(refuri.getFragment(), refdoc);
}
/**
*
* Replace all JSON references, as per the http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 specification, by
* their referants.
*
*
* @param json
* @param duplicate
* @param done
* @return
*/
static Json expandReferences(Json json,
Json topdoc,
URI base,
Map resolved,
Map expanded,
Function uriResolver) throws Exception {
if (expanded.containsKey(json)) return json;
if (json.isObject()) {
if (json.has("id") && json.at("id").isString()) // change scope of nest references
{
base = base.resolve(json.at("id").asString());
}
if (json.has("$ref")) {
URI refuri = makeAbsolute(base, json.at("$ref").asString()); // base.resolve(json.at("$ref").asString());
Json ref = resolved.get(refuri.toString());
if (ref == null) {
ref = Json.object();
resolved.put(refuri.toString(), ref);
ref.with(resolveRef(base, topdoc, refuri, resolved, expanded, uriResolver));
}
json = ref;
} else {
for (Map.Entry e : json.asJsonMap().entrySet())
json.set(e.getKey(), expandReferences(e.getValue(), topdoc, base, resolved, expanded, uriResolver));
}
} else if (json.isArray()) {
for (int i = 0; i < json.asJsonList().size(); i++)
json.set(i,
expandReferences(json.at(i), topdoc, base, resolved, expanded, uriResolver));
}
expanded.put(json, json);
return json;
}
static class DefaultSchema implements Schema {
interface Instruction extends Function {
}
static Json maybeError(Json errors, Json E) {
return E == null ? errors : (errors == null ? Json.array() : errors).with(E, new Json[0]);
}
// Anything is valid schema
static Instruction any = param -> null;
// Type validation
class IsObject implements Instruction {
public Json apply(Json param) {
return param.isObject() ? null : Json.make(param.toString(maxchars));
}
}
class IsArray implements Instruction {
public Json apply(Json param) {
return param.isArray() ? null : Json.make(param.toString(maxchars));
}
}
class IsString implements Instruction {
public Json apply(Json param) {
return param.isString() ? null : Json.make(param.toString(maxchars));
}
}
class IsBoolean implements Instruction {
public Json apply(Json param) {
return param.isBoolean() ? null : Json.make(param.toString(maxchars));
}
}
class IsNull implements Instruction {
public Json apply(Json param) {
return param.isNull() ? null : Json.make(param.toString(maxchars));
}
}
class IsNumber implements Instruction {
public Json apply(Json param) {
return param.isNumber() ? null : Json.make(param.toString(maxchars));
}
}
class IsInteger implements Instruction {
public Json apply(Json param) {
return param.isNumber() && ((Number) param.getValue()) instanceof Integer ? null : Json.make(param.toString(maxchars));
}
}
class CheckString implements Instruction {
int min = 0, max = Integer.MAX_VALUE;
Pattern pattern;
public Json apply(Json param) {
Json errors = null;
if (!param.isString()) return errors;
String s = param.asString();
final int size = s.codePointCount(0, s.length());
if (size < min || size > max)
errors = maybeError(errors, Json.make("String " + param.toString(maxchars) +
" has length outside of the permitted range [" + min + "," + max + "]."));
if (pattern != null && !pattern.matcher(s).matches())
errors = maybeError(errors, Json.make("String " + param.toString(maxchars) +
" does not match regex " + pattern.toString()));
return errors;
}
}
static class CheckNumber implements Instruction {
double min = Double.NaN, max = Double.NaN, multipleOf = Double.NaN;
boolean exclusiveMin = false, exclusiveMax = false;
public Json apply(Json param) {
Json errors = null;
if (!param.isNumber()) return errors;
double value = param.asDouble();
if (!Double.isNaN(min) && (value < min || exclusiveMin && value == min))
errors = maybeError(errors, Json.make("Number " + param + " is below allowed minimum " + min));
if (!Double.isNaN(max) && (value > max || exclusiveMax && value == max))
errors = maybeError(errors, Json.make("Number " + param + " is above allowed maximum " + max));
if (!Double.isNaN(multipleOf) && (value / multipleOf) % 1 != 0)
errors = maybeError(errors, Json.make("Number " + param + " is not a multiple of " + multipleOf));
return errors;
}
}
class CheckArray implements Instruction {
int min = 0, max = Integer.MAX_VALUE;
Boolean uniqueitems = null;
Instruction additionalSchema = any;
Instruction schema;
ArrayList schemas;
public Json apply(Json param) {
Json errors = null;
if (!param.isArray()) return errors;
if (schema == null && schemas == null && additionalSchema == null) // no schema specified
return errors;
int size = param.asJsonList().size();
for (int i = 0; i < size; i++) {
Instruction S = schema != null ? schema
: (schemas != null && i < schemas.size()) ? schemas.get(i) : additionalSchema;
if (S == null)
errors = maybeError(errors, Json.make("Additional items are not permitted: " +
param.at(i) + " in " + param.toString(maxchars)));
else
errors = maybeError(errors, S.apply(param.at(i)));
if (uniqueitems != null && uniqueitems && param.asJsonList().lastIndexOf(param.at(i)) > i)
errors = maybeError(errors, Json.make("Element " + param.at(i) + " is duplicate in array."));
if (errors != null && !errors.asJsonList().isEmpty())
break;
}
if (size < min || size > max)
errors = maybeError(errors, Json.make("Array " + param.toString(maxchars) +
" has number of elements outside of the permitted range [" + min + "," + max + "]."));
return errors;
}
}
class CheckPropertyPresent implements Instruction {
String propname;
public CheckPropertyPresent(String propname) {
this.propname = propname;
}
public Json apply(Json param) {
if (!param.isObject()) return null;
if (param.has(propname)) return null;
else return Json.array().add(Json.make("Required property " + propname +
" missing from object " + param.toString(maxchars)));
}
}
class CheckObject implements Instruction {
int min = 0, max = Integer.MAX_VALUE;
Instruction additionalSchema = any;
ArrayList props = new ArrayList();
ArrayList patternProps = new ArrayList();
// Object validation
class CheckProperty implements Instruction {
String name;
Instruction schema;
public CheckProperty(String name, Instruction schema) {
this.name = name;
this.schema = schema;
}
public Json apply(Json param) {
Json value = param.at(name);
if (value == null)
return null;
else
return schema.apply(param.at(name));
}
}
class CheckPatternProperty // implements Instruction
{
Pattern pattern;
Instruction schema;
public CheckPatternProperty(String pattern, Instruction schema) {
this.pattern = Pattern.compile(pattern);
this.schema = schema;
}
public Json apply(Json param, Set found) {
Json errors = null;
for (Map.Entry e : param.asJsonMap().entrySet())
if (pattern.matcher(e.getKey()).find()) {
found.add(e.getKey());
errors = maybeError(errors, schema.apply(e.getValue()));
}
return errors;
}
}
public Json apply(Json param) {
Json errors = null;
if (!param.isObject()) return errors;
HashSet checked = new HashSet();
for (CheckProperty I : props) {
if (param.has(I.name)) checked.add(I.name);
errors = maybeError(errors, I.apply(param));
}
for (CheckPatternProperty I : patternProps) {
errors = maybeError(errors, I.apply(param, checked));
}
if (additionalSchema != any) for (Map.Entry e : param.asJsonMap().entrySet())
if (!checked.contains(e.getKey()))
errors = maybeError(errors, additionalSchema == null ?
Json.make("Extra property '" + e.getKey() +
"', schema doesn't allow any properties not explicitly defined:" +
param.toString(maxchars))
: additionalSchema.apply(e.getValue()));
if (param.asJsonMap().size() < min)
errors = maybeError(errors, Json.make("Object " + param.toString(maxchars) +
" has fewer than the permitted " + min + " number of properties."));
if (param.asJsonMap().size() > max)
errors = maybeError(errors, Json.make("Object " + param.toString(maxchars) +
" has more than the permitted " + min + " number of properties."));
return errors;
}
}
static class Sequence implements Instruction {
ArrayList seq = new ArrayList();
public Json apply(Json param) {
Json errors = null;
for (Instruction I : seq)
errors = maybeError(errors, I.apply(param));
return errors;
}
public Sequence add(Instruction I) {
seq.add(I);
return this;
}
}
class CheckType implements Instruction {
Json types;
public CheckType(Json types) {
this.types = types;
}
public Json apply(Json param) {
String ptype = param.isString() ? "string" :
param.isObject() ? "object" :
param.isArray() ? "array" :
param.isNumber() ? "number" :
param.isNull() ? "null" : "boolean";
for (Json type : types.asJsonList())
if (type.asString().equals(ptype))
return null;
else if (type.asString().equals("integer") &&
param.isNumber() &&
param.asDouble() % 1 == 0)
return null;
return Json.array().add(Json.make("Type mistmatch for " + param.toString(maxchars) +
", allowed types: " + types));
}
}
class CheckEnum implements Instruction {
Json theenum;
public CheckEnum(Json theenum) {
this.theenum = theenum;
}
public Json apply(Json param) {
for (Json option : theenum.asJsonList())
if (param.equals(option))
return null;
return Json.array().add("Element " + param.toString(maxchars) +
" doesn't match any of enumerated possibilities " + theenum);
}
}
class CheckAny implements Instruction {
ArrayList alternates = new ArrayList();
Json schema;
public Json apply(Json param) {
for (Instruction I : alternates)
if (I.apply(param) == null)
return null;
return Json.array().add("Element " + param.toString(maxchars) +
" must conform to at least one of available sub-schemas " +
schema.toString(maxchars));
}
}
class CheckOne implements Instruction {
ArrayList alternates = new ArrayList();
Json schema;
public Json apply(Json param) {
int matches = 0;
Json errors = Json.array();
for (Instruction I : alternates) {
Json result = I.apply(param);
if (result == null)
matches++;
else
errors.add(result);
}
if (matches != 1) {
return Json.array().add("Element " + param.toString(maxchars) +
" must conform to exactly one of available sub-schemas, but not more " +
schema.toString(maxchars)).add(errors);
} else
return null;
}
}
class CheckNot implements Instruction {
Instruction I;
Json schema;
public CheckNot(Instruction I, Json schema) {
this.I = I;
this.schema = schema;
}
public Json apply(Json param) {
if (I.apply(param) != null)
return null;
else
return Json.array().add("Element " + param.toString(maxchars) +
" must NOT conform to the schema " + schema.toString(maxchars));
}
}
static class CheckSchemaDependency implements Instruction {
Instruction schema;
String property;
public CheckSchemaDependency(String property, Instruction schema) {
this.property = property;
this.schema = schema;
}
public Json apply(Json param) {
if (!param.isObject()) return null;
else if (!param.has(property)) return null;
else return (schema.apply(param));
}
}
class CheckPropertyDependency implements Instruction {
Json required;
String property;
public CheckPropertyDependency(String property, Json required) {
this.property = property;
this.required = required;
}
public Json apply(Json param) {
if (!param.isObject()) return null;
if (!param.has(property)) return null;
else {
Json errors = null;
for (Json p : required.asJsonList())
if (!param.has(p.asString()))
errors = maybeError(errors, Json.make("Conditionally required property " + p +
" missing from object " + param.toString(maxchars)));
return errors;
}
}
}
Instruction compile(Json S, Map compiled) {
Instruction result = compiled.get(S);
if (result != null)
return result;
Sequence seq = new Sequence();
compiled.put(S, seq);
if (S.has("type") && !S.is("type", "any"))
seq.add(new CheckType(S.at("type").isString() ?
Json.array().add(S.at("type")) : S.at("type")));
if (S.has("enum"))
seq.add(new CheckEnum(S.at("enum")));
if (S.has("allOf")) {
Sequence sub = new Sequence();
for (Json x : S.at("allOf").asJsonList())
sub.add(compile(x, compiled));
seq.add(sub);
}
if (S.has("anyOf")) {
CheckAny any = new CheckAny();
any.schema = S.at("anyOf");
for (Json x : any.schema.asJsonList())
any.alternates.add(compile(x, compiled));
seq.add(any);
}
if (S.has("oneOf")) {
CheckOne any = new CheckOne();
any.schema = S.at("oneOf");
for (Json x : any.schema.asJsonList())
any.alternates.add(compile(x, compiled));
seq.add(any);
}
if (S.has("not"))
seq.add(new CheckNot(compile(S.at("not"), compiled), S.at("not")));
if (S.has("required") && S.at("required").isArray()) {
for (Json p : S.at("required").asJsonList())
seq.add(new CheckPropertyPresent(p.asString()));
}
CheckObject objectCheck = new CheckObject();
if (S.has("properties"))
for (Map.Entry p : S.at("properties").asJsonMap().entrySet())
objectCheck.props.add(objectCheck.new CheckProperty(
p.getKey(), compile(p.getValue(), compiled)));
if (S.has("patternProperties"))
for (Map.Entry p : S.at("patternProperties").asJsonMap().entrySet())
objectCheck.patternProps.add(objectCheck.new CheckPatternProperty(p.getKey(),
compile(p.getValue(), compiled)));
if (S.has("additionalProperties")) {
if (S.at("additionalProperties").isObject())
objectCheck.additionalSchema = compile(S.at("additionalProperties"), compiled);
else if (!S.at("additionalProperties").asBoolean())
objectCheck.additionalSchema = null; // means no additional properties allowed
}
if (S.has("minProperties"))
objectCheck.min = S.at("minProperties").asInteger();
if (S.has("maxProperties"))
objectCheck.max = S.at("maxProperties").asInteger();
if (!objectCheck.props.isEmpty() || !objectCheck.patternProps.isEmpty() ||
objectCheck.additionalSchema != any ||
objectCheck.min > 0 || objectCheck.max < Integer.MAX_VALUE)
seq.add(objectCheck);
CheckArray arrayCheck = new CheckArray();
if (S.has("items"))
if (S.at("items").isObject())
arrayCheck.schema = compile(S.at("items"), compiled);
else {
arrayCheck.schemas = new ArrayList();
for (Json s : S.at("items").asJsonList())
arrayCheck.schemas.add(compile(s, compiled));
}
if (S.has("additionalItems"))
if (S.at("additionalItems").isObject())
arrayCheck.additionalSchema = compile(S.at("additionalItems"), compiled);
else if (!S.at("additionalItems").asBoolean())
arrayCheck.additionalSchema = null;
if (S.has("uniqueItems"))
arrayCheck.uniqueitems = S.at("uniqueItems").asBoolean();
if (S.has("minItems"))
arrayCheck.min = S.at("minItems").asInteger();
if (S.has("maxItems"))
arrayCheck.max = S.at("maxItems").asInteger();
if (arrayCheck.schema != null || arrayCheck.schemas != null ||
arrayCheck.additionalSchema != any ||
arrayCheck.uniqueitems != null ||
arrayCheck.max < Integer.MAX_VALUE || arrayCheck.min > 0)
seq.add(arrayCheck);
CheckNumber numberCheck = new CheckNumber();
if (S.has("minimum"))
numberCheck.min = S.at("minimum").asDouble();
if (S.has("maximum"))
numberCheck.max = S.at("maximum").asDouble();
if (S.has("multipleOf"))
numberCheck.multipleOf = S.at("multipleOf").asDouble();
if (S.has("exclusiveMinimum"))
numberCheck.exclusiveMin = S.at("exclusiveMinimum").asBoolean();
if (S.has("exclusiveMaximum"))
numberCheck.exclusiveMax = S.at("exclusiveMaximum").asBoolean();
if (!Double.isNaN(numberCheck.min) || !Double.isNaN(numberCheck.max) || !Double.isNaN(numberCheck.multipleOf))
seq.add(numberCheck);
CheckString stringCheck = new CheckString();
if (S.has("minLength"))
stringCheck.min = S.at("minLength").asInteger();
if (S.has("maxLength"))
stringCheck.max = S.at("maxLength").asInteger();
if (S.has("pattern"))
stringCheck.pattern = Pattern.compile(S.at("pattern").asString());
if (stringCheck.min > 0 || stringCheck.max < Integer.MAX_VALUE || stringCheck.pattern != null)
seq.add(stringCheck);
if (S.has("dependencies"))
for (Map.Entry e : S.at("dependencies").asJsonMap().entrySet())
if (e.getValue().isObject())
seq.add(new CheckSchemaDependency(e.getKey(), compile(e.getValue(), compiled)));
else if (e.getValue().isArray())
seq.add(new CheckPropertyDependency(e.getKey(), e.getValue()));
else
seq.add(new CheckPropertyDependency(e.getKey(), Json.array(e.getValue())));
result = seq.seq.size() == 1 ? seq.seq.get(0) : seq;
compiled.put(S, result);
return result;
}
int maxchars = 50;
URI uri;
Json theschema;
Instruction start;
DefaultSchema(URI uri, Json theschema, Function relativeReferenceResolver) {
try {
this.uri = uri == null ? new URI("") : uri;
if (relativeReferenceResolver == null)
relativeReferenceResolver = docuri -> {
try {
return Json.read(fetchContent(docuri.toURL()));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
this.theschema = theschema.dup();
this.theschema = expandReferences(this.theschema,
this.theschema,
this.uri,
new HashMap(),
new IdentityHashMap(),
relativeReferenceResolver);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
this.start = compile(this.theschema, new IdentityHashMap());
}
public Json validate(Json document) {
Json result = Json.object("ok", true);
Json errors = start.apply(document);
return errors == null ? result : result.set("errors", errors).set("ok", false);
}
public Json toJson() {
return theschema;
}
public Json generate(Json options) {
// TODO...
return Json.nil();
}
}
public static Schema schema(Json S) {
return new DefaultSchema(null, S, null);
}
public static Schema schema(URI uri) {
return schema(uri, null);
}
public static Schema schema(URI uri, Function relativeReferenceResolver) {
try {
return new DefaultSchema(uri, Json.read(Json.fetchContent(uri.toURL())), relativeReferenceResolver);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Schema schema(Json S, URI uri) {
return new DefaultSchema(uri, S, null);
}
public static class DefaultFactory implements Factory {
public Json nil() {
return new NullJson();
}
public Json bool(boolean x) {
return new BooleanJson(x ? Boolean.TRUE : Boolean.FALSE, null);
}
public Json string(String x) {
return new StringJson(x, null);
}
public Json raw(String x) {
return new RawJson(x, null);
}
public Json number(Number x) {
return new NumberJson(x, null);
}
public Json array() {
return new ArrayJson();
}
public Json object() {
return new ObjectJson();
}
public Json make(Object anything) {
if (anything == null)
return nil();
else if (anything instanceof Properties) {
Properties properties = (Properties) anything;
Json O = object();
for (Map.Entry, ?> x : properties.entrySet())
O.set(x.getKey().toString(), factory().make(x.getValue()));
return O;
} else if (anything instanceof Json)
return (Json) anything;
else if (anything instanceof String)
return factory().string((String) anything);
else if (anything instanceof JsonSerialization)
return ((JsonSerialization) anything).toJson();
else if (anything instanceof Class>)
return factory().string(((Class>) anything).getName());
else if (anything instanceof Collection>) {
Json L = array();
for (Object x : (Collection>) anything)
L.add(factory().make(x));
return L;
} else if (anything instanceof Map, ?>) {
Json O = object();
for (Map.Entry, ?> x : ((Map, ?>) anything).entrySet())
O.set(x.getKey().toString(), factory().make(x.getValue()));
return O;
} else if (anything instanceof Boolean)
return factory().bool((Boolean) anything);
else if (anything instanceof Number)
return factory().number((Number) anything);
else if (anything instanceof Enum) {
return factory().string(anything.toString());
} else if (anything instanceof MediaType) {
return factory().string(anything.toString());
} else if (anything instanceof Instant) {
return factory().string(anything.toString());
} else if (anything.getClass().isArray()) {
Class> comp = anything.getClass().getComponentType();
if (!comp.isPrimitive())
return Json.array((Object[]) anything);
Json A = array();
if (boolean.class == comp)
for (boolean b : (boolean[]) anything) A.add(b);
else if (byte.class == comp)
for (byte b : (byte[]) anything) A.add(b);
else if (char.class == comp)
for (char b : (char[]) anything) A.add(b);
else if (short.class == comp)
for (short b : (short[]) anything) A.add(b);
else if (int.class == comp)
for (int b : (int[]) anything) A.add(b);
else if (long.class == comp)
for (long b : (long[]) anything) A.add(b);
else if (float.class == comp)
for (float b : (float[]) anything) A.add(b);
else if (double.class == comp)
for (double b : (double[]) anything) A.add(b);
return A;
} else if (anything instanceof Principal) {
return factory().string(((Principal) anything).getName());
} else
throw new IllegalArgumentException("Don't know how to convert to Json : " + anything);
}
}
public static final Factory defaultFactory = new DefaultFactory();
private static Factory globalFactory = defaultFactory;
// TODO: maybe use initialValue thread-local method to attach global factory by default here...
private static final ThreadLocal threadFactory = new ThreadLocal();
/**
* Return the {@link Factory} currently in effect. This is the factory that the {@link #make(Object)} method
* will dispatch on upon determining the type of its argument. If you already know the type of element to construct,
* you can avoid the type introspection implicit to the make method and call the factory directly. This will result
* in an optimization.
*
* @return the factory
*/
public static Factory factory() {
Factory f = threadFactory.get();
return f != null ? f : globalFactory;
}
public static void escape(CharSequence sequence, Appendable out) throws IOException {
escaper.escapeJsonString(sequence, out);
}
/**
*
* Specify a global Json {@link Factory} to be used by all threads that don't have a specific thread-local factory
* attached to them.
*
*
* @param factory The new global factory
*/
public static void setGlobalFactory(Factory factory) {
globalFactory = factory;
}
/**
*
* Attach a thread-local Json {@link Factory} to be used specifically by this thread. Thread-local Json factories are
* the only means to have different {@link Factory} implementations used simultaneously in the same application
* (well, more accurately, the same ClassLoader).
*
*
* @param factory the new thread local factory
*/
public static void attachFactory(Factory factory) {
threadFactory.set(factory);
}
/**
*
* Clear the thread-local factory previously attached to this thread via the {@link #attachFactory(Factory)} method.
* The global factory takes effect after a call to this method.
*
*/
public static void detachFactory() {
threadFactory.remove();
}
/**
*
* Parse a JSON entity from its string representation.
*
*
* @param jsonAsString A valid JSON representation as per the json.org grammar.
* Cannot be null
.
* @return The JSON entity parsed: an object, array, string, number or boolean, or null. Note that this method will
* never return the actual Java null
.
*/
public static Json read(String jsonAsString) {
return (Json) new Reader().read(jsonAsString);
}
/**
*
* Parse a JSON entity from a URL
.
*
*
* @param location A valid URL where to load a JSON document from. Cannot be null
.
* @return The JSON entity parsed: an object, array, string, number or boolean, or null. Note that this method will
* never return the actual Java null
.
*/
public static Json read(URL location) {
return (Json) new Reader().read(fetchContent(location));
}
/**
*
* Parse a JSON entity from a {@link CharacterIterator}.
*
*
* @param it A character iterator.
* @return the parsed JSON element
* @see #read(String)
*/
public static Json read(CharacterIterator it) {
return (Json) new Reader().read(it);
}
/**
* @return the null Json
instance.
*/
public static Json nil() {
return factory().nil();
}
/**
* @return a newly constructed, empty JSON object.
*/
public static Json object() {
return factory().object();
}
/**
* Return a new JSON object initialized from the passed list of
* name/value pairs. The number of arguments must be even. Each argument at an even position is taken to be a name
* for the following value. The name arguments are normally of type Java String, but they can be of any other type
* having an appropriate
* toString
method. Each value is first converted
* to a Json
instance using the {@link #make(Object)} method.
*
*
* @param args A sequence of name value pairs.
* @return the new JSON object.
*/
public static Json object(Object... args) {
Json j = object();
if (args.length % 2 != 0)
throw new IllegalArgumentException("An even number of arguments is expected.");
for (int i = 0; i < args.length; i++)
j.set(args[i].toString(), factory().make(args[++i]));
return j;
}
/**
* @return a new constructed, empty JSON array.
*/
public static Json array() {
return factory().array();
}
/**
* Return a new JSON array filled up with the list of arguments.
*
* @param args The initial content of the array.
* @return the new JSON array
*/
public static Json array(Object... args) {
Json A = array();
for (Object x : args)
A.add(factory().make(x));
return A;
}
/**
*
* Exposes some internal methods that are useful for {@link Json.Factory} implementations or other extension/layers
* of the library.
*
*
* @author Borislav Iordanov
*/
public static class help {
/**
*
* Perform JSON escaping so that ", <, >, etc. characters are properly encoded in the JSON string representation
* before returning to the client code. This is useful when serializing property names or string values.
*
*/
public static String escape(String string) {
return escaper.escapeJsonString(string);
}
/**
*
* Given a JSON Pointer, as per RFC 6901, return the nested JSON value within the element
parameter.
*
*/
public static Json resolvePointer(String pointer, Json element) {
return Json.resolvePointer(pointer, element);
}
}
/**
*
* Convert an arbitrary Java instance to a {@link Json} instance.
*
*
*
* Maps, Collections and arrays are recursively copied where each of their elements concerted into Json
* instances as well. The keys of a {@link Map} parameter are normally strings, but anything with a meaningful
* toString
implementation will work as well.
*
*
* @param anything Any Java object that the current JSON factory in effect is capable of handling.
* @return The Json
. This method will never return null
. It will throw an
* {@link IllegalArgumentException} if it doesn't know how to convert the argument to a Json
instance.
* @throws IllegalArgumentException when the concrete type of the parameter is unknown.
*/
public static Json make(Object anything) {
return factory().make(anything);
}
// end of static utility method section
Json enclosing = null;
int line;
int column;
protected Json() {
}
protected Json(Json enclosing) {
this.enclosing = enclosing;
}
public Json location(int line, int column) {
this.line = line;
this.column = column;
return this;
}
public int getLine() {
return line;
}
public int getColumn() {
return column;
}
/**
* Return a string representation of this
that does
* not exceed a certain maximum length. This is useful in constructing error messages or any other place where only a
* "preview" of the JSON element should be displayed. Some JSON structures can get very large and this method will
* help avoid string serializing the whole of them.
*
* @param maxCharacters The maximum number of characters for the string representation.
* @return The string representation of this object.
*/
public String toString(int maxCharacters) {
return toString();
}
public String toPrettyString() {
return toString();
}
/**
* Explicitly set the parent of this element. The parent is presumably an array
* or an object. Normally, there's no need to call this method as the parent is automatically set by the framework.
* You may need to call it however, if you implement your own {@link Factory} with your own implementations of the
* Json types.
*
*
* @param enclosing The parent element.
*/
public void attachTo(Json enclosing) {
this.enclosing = enclosing;
}
/**
* @return the Json
entity, if any, enclosing this
* Json
. The returned value can be null
or
* a Json
object or list, but not one of the primitive types.
*/
public final Json up() {
return enclosing;
}
/**
* @return a clone (a duplicate) of this Json
entity. Note that cloning is deep if array and objects.
* Primitives are also cloned, even though their values are immutable because the new enclosing entity (the result of
* the {@link #up()} method) may be different. since they are immutable.
*/
public Json dup() {
return this;
}
/**
* Return the Json
element at the specified index of this
* Json
array. This method applies only to Json arrays.
*
*
* @param index The index of the desired element.
* @return The JSON element at the specified index in this array.
*/
public Json at(int index) {
throw new UnsupportedOperationException();
}
/**
*
* Return the specified property of a Json
object or null
if there's no such property. This
* method applies only to Json objects.
*
*
* @param property the property name.
* @return The JSON element that is the value of that property.
*/
public Json at(String property) {
throw new UnsupportedOperationException();
}
/**
*
* Return the specified property of a Json
object if it exists. If it doesn't, then create a new
* property with value the def
parameter and return that parameter.
*
*
* @param property The property to return.
* @param def The default value to set and return in case the property doesn't exist.
*/
public final Json at(String property, Json def) {
Json x = at(property);
if (x == null) {
return def;
} else
return x;
}
/**
*
* Return the specified property of a Json
object if it exists. If it doesn't, then create a new
* property with value the def
parameter and return that parameter.
*
*
* @param property The property to return.
* @param def The default value to set and return in case the property doesn't exist.
*/
public final Json at(String property, Object def) {
return at(property, make(def));
}
/**
*
* Return true if this Json
object has the specified property and false otherwise.
*
*
* @param property The name of the property.
*/
public boolean has(String property) {
throw new UnsupportedOperationException();
}
/**
*
* Return true
if and only if this Json
object has a property with the specified value. In
* particular, if the object has no such property false
is returned.
*
*
* @param property The property name.
* @param value The value to compare with. Comparison is done via the equals method. If the value is not an
* instance of Json
, it is first converted to such an instance.
* @return
*/
public boolean is(String property, Object value) {
throw new UnsupportedOperationException();
}
/**
*
* Return true
if and only if this Json
array has an element with the specified value at
* the specified index. In particular, if the array has no element at this index, false
is returned.
*
*
* @param index The 0-based index of the element in a JSON array.
* @param value The value to compare with. Comparison is done via the equals method. If the value is not an instance
* of Json
, it is first converted to such an instance.
* @return
*/
public boolean is(int index, Object value) {
throw new UnsupportedOperationException();
}
/**
*
* Add the specified Json
element to this array.
*
*
* @return this
*/
public Json add(Json el) {
throw new UnsupportedOperationException();
}
/**
*
* Add an arbitrary Java object to this Json
array. The object is first converted to a Json
* instance by calling the static {@link #make} method.
*
*
* @param anything Any Java object that can be converted to a Json instance.
* @return this
*/
public final Json add(Object anything) {
return add(make(anything));
}
/**
*
* Remove the specified property from a Json
object and return that property.
*
*
* @param property The property to be removed.
* @return The property value or null
if the object didn't have such a property to begin with.
*/
public Json atDel(String property) {
throw new UnsupportedOperationException();
}
/**
*
* Remove the element at the specified index from a Json
array and return that element.
*
*
* @param index The index of the element to delete.
* @return The element value.
*/
public Json atDel(int index) {
throw new UnsupportedOperationException();
}
/**
*
* Delete the specified property from a Json
object.
*
*
* @param property The property to be removed.
* @return this
*/
public Json delAt(String property) {
throw new UnsupportedOperationException();
}
/**
*
* Remove the element at the specified index from a Json
array.
*
*
* @param index The index of the element to delete.
* @return this
*/
public Json delAt(int index) {
throw new UnsupportedOperationException();
}
/**
*
* Remove the specified element from a Json
array.
*
*
* @param el The element to delete.
* @return this
*/
public Json remove(Json el) {
throw new UnsupportedOperationException();
}
/**
*
* Remove the specified Java object (converted to a Json instance) from a Json
array. This is equivalent
* to
* remove({@link #make(Object)})
.
*
*
* @param anything The object to delete.
* @return this
*/
public final Json remove(Object anything) {
return remove(make(anything));
}
/**
*
* Set a Json
objects's property.
*
*
* @param property The property name.
* @param value The value of the property.
* @return this
*/
public Json set(String property, Json value) {
throw new UnsupportedOperationException();
}
/**
*
* Set a Json
objects's property.
*
*
* @param property The property name.
* @param value The value of the property, converted to a Json
representation with {@link #make}.
* @return this
*/
public final Json set(String property, Object value) {
return set(property, make(value));
}
/**
*
* Change the value of a JSON array element. This must be an array.
*
*
* @param index 0-based index of the element in the array.
* @param value the new value of the element
* @return this
*/
public Json set(int index, Object value) {
throw new UnsupportedOperationException();
}
/**
*
* Combine this object or array with the passed in object or array. The types of
* this
and the object
argument must match. If both are
* Json
objects, all properties of the parameter are added to this
.
* If both are arrays, all elements of the parameter are appended to this
*
*
* @param object The object or array whose properties or elements must be added to this Json object or array.
* @param options A sequence of options that governs the merging process.
* @return this
*/
public Json with(Json object, Json[] options) {
throw new UnsupportedOperationException();
}
/**
* Same as {}@link #with(Json,Json...options)}
with each option argument converted to Json
* first.
*/
public Json with(Json object, Object... options) {
Json[] jopts = new Json[options.length];
for (int i = 0; i < jopts.length; i++)
jopts[i] = make(options[i]);
return with(object, jopts);
}
public Json replace(Json oldJson, Json newJson) {
if (this.isObject()) {
for (Map.Entry entry : this.asJsonMap().entrySet()) {
if (entry.getValue() == oldJson) {
entry.setValue(newJson);
return newJson;
}
}
throw new IllegalArgumentException();
} else {
throw new UnsupportedOperationException();
}
}
/**
* @return the underlying value of this Json
entity. The actual value will be a Java Boolean, String,
* Number, Map, List or null. For complex entities (objects or arrays), the method will perform a deep copy and extra
* underlying values recursively for all nested elements.
*/
public Object getValue() {
throw new UnsupportedOperationException();
}
/**
* @return the boolean value of a boolean Json
instance. Call {@link #isBoolean()} first if you're not
* sure this instance is indeed a boolean.
*/
public boolean asBoolean() {
throw new UnsupportedOperationException();
}
/**
* @return the string value of a string Json
instance. Call {@link #isString()} first if you're not sure
* this instance is indeed a string.
*/
public String asString() {
throw new UnsupportedOperationException();
}
/**
* @return the integer value of a number Json
instance. Call {@link #isNumber()} first if you're not
* sure this instance is indeed a number.
*/
public int asInteger() {
throw new UnsupportedOperationException();
}
/**
* @return the float value of a float Json
instance. Call {@link #isNumber()} first if you're not sure
* this instance is indeed a number.
*/
public float asFloat() {
throw new UnsupportedOperationException();
}
/**
* @return the double value of a number Json
instance. Call {@link #isNumber()} first if you're not sure
* this instance is indeed a number.
*/
public double asDouble() {
throw new UnsupportedOperationException();
}
/**
* @return the long value of a number Json
instance. Call {@link #isNumber()} first if you're not sure
* this instance is indeed a number.
*/
public long asLong() {
throw new UnsupportedOperationException();
}
/**
* @return the short value of a number Json
instance. Call {@link #isNumber()} first if you're not sure
* this instance is indeed a number.
*/
public short asShort() {
throw new UnsupportedOperationException();
}
/**
* @return the byte value of a number Json
instance. Call {@link #isNumber()} first if you're not sure
* this instance is indeed a number.
*/
public byte asByte() {
throw new UnsupportedOperationException();
}
/**
* @return the first character of a string Json
instance. Call {@link #isString()} first if you're not
* sure this instance is indeed a string.
*/
public char asChar() {
throw new UnsupportedOperationException();
}
/**
* @return a map of the properties of an object Json
instance. The map is a clone of the object and can
* be modified safely without affecting it. Call {@link #isObject()} first if you're not sure this instance is indeed
* a
* Json
object.
*/
public Map asMap() {
throw new UnsupportedOperationException();
}
/**
* @return the underlying map of properties of a Json
object. The returned map is the actual object
* representation so any modifications to it are modifications of the Json
object itself. Call
* {@link #isObject()} first if you're not sure this instance is indeed a
* Json
object.
*/
public Map asJsonMap() {
throw new UnsupportedOperationException();
}
/**
* @return a list of the elements of a Json
array. The list is a clone of the array and can be modified
* safely without affecting it. Call {@link #isArray()} first if you're not sure this instance is indeed a
* Json
array.
*/
public List