@Override public JsonReader peekJson() { return new JsonValueReader(this); }
@Override public double nextDouble() throws IOException { Object peeked = require(Object.class, Token.NUMBER); double result; if (peeked instanceof Number) { result = ((Number) peeked).doubleValue(); } else if (peeked instanceof String) { try { result = Double.parseDouble((String) peeked); } catch (NumberFormatException e) { throw typeMismatch(peeked, Token.NUMBER); } } else { throw typeMismatch(peeked, Token.NUMBER); } if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) { throw new JsonEncodingException("JSON forbids NaN and infinities: " + result + " at path " + getPath()); } remove(); return result; }
@Override public String nextName() throws IOException { Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME); // Swap the Map.Entry for its value on the stack and return its key. String result = stringKey(peeked); stack[stackSize - 1] = peeked.getValue(); pathNames[stackSize - 2] = result; return result; }
@Override public void endArray() throws IOException { JsonIterator peeked = require(JsonIterator.class, Token.END_ARRAY); if (peeked.endToken != Token.END_ARRAY || peeked.hasNext()) { throw typeMismatch(peeked, Token.END_ARRAY); } remove(); }
@Override public void skipName() throws IOException { if (failOnUnknown) { throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); } Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME); // Swap the Map.Entry for its value on the stack. stack[stackSize - 1] = peeked.getValue(); pathNames[stackSize - 2] = "null"; }
@Override public void skipValue() throws IOException { if (failOnUnknown) { throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); } // If this element is in an object clear out the key. if (stackSize > 1) { pathNames[stackSize - 2] = "null"; } Object skipped = stackSize != 0 ? stack[stackSize - 1] : null; if (skipped instanceof JsonIterator) { throw new JsonDataException("Expected a value but was " + peek() + " at path " + getPath()); } if (skipped instanceof Map.Entry) { // We're skipping a name. Promote the map entry's value. Map.Entry<?, ?> entry = (Map.Entry<?, ?>) stack[stackSize - 1]; stack[stackSize - 1] = entry.getValue(); } else if (stackSize > 0) { // We're skipping a value. remove(); } else { throw new JsonDataException("Expected a value but was " + peek() + " at path " + getPath()); } }
@Override public void beginObject() throws IOException { Map<?, ?> peeked = require(Map.class, Token.BEGIN_OBJECT); JsonIterator iterator = new JsonIterator( Token.END_OBJECT, peeked.entrySet().toArray(new Object[peeked.size()]), 0); stack[stackSize - 1] = iterator; scopes[stackSize - 1] = JsonScope.EMPTY_OBJECT; // If the iterator isn't empty push its first value onto the stack. if (iterator.hasNext()) { push(iterator.next()); } }
@Override public String nextString() throws IOException { Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null); if (peeked instanceof String) { remove(); return (String) peeked; } if (peeked instanceof Number) { remove(); return peeked.toString(); } if (peeked == JSON_READER_CLOSED) { throw new IllegalStateException("JsonReader is closed"); } throw typeMismatch(peeked, Token.STRING); }
private String stringKey(Map.Entry<?, ?> entry) { Object name = entry.getKey(); if (name instanceof String) return (String) name; throw typeMismatch(name, Token.NAME); }
private void push(Object newTop) { if (stackSize == stack.length) { if (stackSize == 256) { throw new JsonDataException("Nesting too deep at " + getPath()); } scopes = Arrays.copyOf(scopes, scopes.length * 2); pathNames = Arrays.copyOf(pathNames, pathNames.length * 2); pathIndices = Arrays.copyOf(pathIndices, pathIndices.length * 2); stack = Arrays.copyOf(stack, stack.length * 2); } stack[stackSize++] = newTop; }
/** * Removes a value and prepares for the next. If we're iterating a map or list this advances the * iterator. */ private void remove() { stackSize--; stack[stackSize] = null; scopes[stackSize] = 0; // If we're iterating an array or an object push its next element on to the stack. if (stackSize > 0) { pathIndices[stackSize - 1]++; Object parent = stack[stackSize - 1]; if (parent instanceof Iterator && ((Iterator<?>) parent).hasNext()) { push(((Iterator<?>) parent).next()); } } }
@Override public int selectString(Options options) throws IOException { Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null); if (!(peeked instanceof String)) { if (peeked == JSON_READER_CLOSED) { throw new IllegalStateException("JsonReader is closed"); } return -1; } String peekedString = (String) peeked; for (int i = 0, length = options.strings.length; i < length; i++) { if (options.strings[i].equals(peekedString)) { remove(); return i; } } return -1; }
@Override public void endObject() throws IOException { JsonIterator peeked = require(JsonIterator.class, Token.END_OBJECT); if (peeked.endToken != Token.END_OBJECT || peeked.hasNext()) { throw typeMismatch(peeked, Token.END_OBJECT); } pathNames[stackSize - 1] = null; remove(); }
@Override public void skipName() throws IOException { if (failOnUnknown) { throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); } Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME); // Swap the Map.Entry for its value on the stack. stack[stackSize - 1] = peeked.getValue(); pathNames[stackSize - 2] = "null"; }
@Override public void skipValue() throws IOException { if (failOnUnknown) { throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath()); } // If this element is in an object clear out the key. if (stackSize > 1) { pathNames[stackSize - 2] = "null"; } Object skipped = stackSize != 0 ? stack[stackSize - 1] : null; if (skipped instanceof Map.Entry) { // We're skipping a name. Promote the map entry's value. Map.Entry<?, ?> entry = (Map.Entry<?, ?>) stack[stackSize - 1]; stack[stackSize - 1] = entry.getValue(); } else if (stackSize > 0) { // We're skipping a value. remove(); } }
@Override public int selectName(Options options) throws IOException { Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME); String name = stringKey(peeked); for (int i = 0, length = options.strings.length; i < length; i++) { // Swap the Map.Entry for its value on the stack and return its key. if (options.strings[i].equals(name)) { stack[stackSize - 1] = peeked.getValue(); pathNames[stackSize - 2] = name; return i; } } return -1; }
@Override public void beginArray() throws IOException { List<?> peeked = require(List.class, Token.BEGIN_ARRAY); JsonIterator iterator = new JsonIterator( Token.END_ARRAY, peeked.toArray(new Object[peeked.size()]), 0); stack[stackSize - 1] = iterator; scopes[stackSize - 1] = JsonScope.EMPTY_ARRAY; pathIndices[stackSize - 1] = 0; // If the iterator isn't empty push its first value onto the stack. if (iterator.hasNext()) { push(iterator.next()); } }