public ParsedDocumentOperation readSingleDocumentStreaming(DocumentOperationType operationType, String docIdString) {
        try {
            DocumentId docId = new DocumentId(docIdString);
            DocumentParseInfo documentParseInfo = new DocumentParseInfo();
            documentParseInfo.documentId = docId;
            documentParseInfo.operationType = operationType;

            if (JsonToken.START_OBJECT != parser.nextValue())
                throw new IllegalArgumentException("expected start of root object, got " + parser.currentToken());

            Boolean create = null;
            String condition = null;
            ParsedDocumentOperation operation = null;
            while (JsonToken.END_OBJECT != parser.nextValue()) {
                switch (parser.currentName()) {
                    case FIELDS -> {
                        documentParseInfo.fieldsBuffer = new LazyTokenBuffer(parser);
                        VespaJsonDocumentReader vespaJsonDocumentReader = new VespaJsonDocumentReader(typeManager.getIgnoreUndefinedFields());
                        operation = vespaJsonDocumentReader.createDocumentOperation(
                                getDocumentTypeFromString(documentParseInfo.documentId.getDocType(), typeManager), documentParseInfo);

                        if ( ! documentParseInfo.fieldsBuffer.isEmpty())
                            throw new IllegalArgumentException("expected all content to be consumed by document parsing, but " +
                                                               documentParseInfo.fieldsBuffer.nesting() + " levels remain");

                    }
                    case CONDITION -> {
                        if ( ! JsonToken.VALUE_STRING.equals(parser.currentToken()) && ! JsonToken.VALUE_NULL.equals(parser.currentToken()))
                            throw new IllegalArgumentException("expected string value for condition, got " + parser.currentToken());

                        condition = parser.getValueAsString();
                    }
                    case CREATE_IF_NON_EXISTENT -> {
                        create = parser.getBooleanValue(); // Throws if not boolean.
                    }
                    default -> {
                        // We ignore stray fields, but need to ensure structural balance in doing do.
                        if (parser.currentToken().isStructStart()) parser.skipChildren();
                    }
                }
            }

            if (null != parser.nextToken())
                throw new IllegalArgumentException("expected end of input, got " + parser.currentToken());

            if (null == operation)
                throw new IllegalArgumentException("document is missing the required \"fields\" field");

            if (null != create) {
                switch (operationType) {
                    case PUT -> ((DocumentPut) operation.operation()).setCreateIfNonExistent(create);
                    case UPDATE -> ((DocumentUpdate) operation.operation()).setCreateIfNonExistent(create);
                    case REMOVE -> throw new IllegalArgumentException(CREATE_IF_NON_EXISTENT + " is not supported for remove operations");
                }
            }

            operation.operation().setCondition(TestAndSetCondition.fromConditionString(Optional.ofNullable(condition)));

            return operation;
        }
        catch (IOException e) {
            throw new IllegalArgumentException("failed parsing document", e);
        }
    }
Reads a JSON which is expected to contain a single document operation,
and where other parameters, like the document ID and operation type, are supplied by other means.

@param operationType the type of operation (update or put)
@param docIdString document ID
@return the parsed document operation
document/src/main/java/com/yahoo/document/json/JsonReader.java