Copybara import of the project:

  - c6f2abbb52eb834b28739d90dbee78b549aa4312 Initial commit by Eclipse Webmaster team <webmaster@eclipse.org>
  - f160a13c1253b6f06205c6082573b115a68a312c Initial contribution by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 63536332cd9e5ded826a8d0af133999c51e3ec61 Facebook resource files updated (#81) by David Král <4483705+Verdent@users.noreply.github.com>
  - d5bded4af804870d46e3bbe4ae8de132258d23a1 Travis CI build. (#83) by Tomáš Kraus <tomas.kraus@oracle.com>
  - f49271c2959703edae66229dd146f9e6dbfb9f91 Yasson Issue #89 - Pretty formatting (#87) by David Král <4483705+Verdent@users.noreply.github.com>
  - c0632f03ddaf8f6592152b4f3e8083cf284564a4 Merge pull request #89 from Tomas-Kraus/master by Roman Grigoriadi <bravehorsie@gmail.com>
  - 6306ef2039497c96919f5207eedf9bede7d3bd07 add NOTICE and CONTRIBUTING files (#93) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 9892c0658f9d55b819d39039a364df37d1d60202 set version to 1.1.3-SNAPSHOT (#94) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - dca81ea88de8248ff06907ce36920055c8517302 Merge pull request #98 from Tomas-Kraus/master by Werner Keil <werner.keil@gmx.net>
  - cb041e37abdb426bd850fe732c7270ae8d0be40b Added .gitignore file (#112) by Cousjava <jonathan.coustick@payara.fish>
  - f1639f44c5d61d5e123b1edf948af4da5a833006 'jakarta' groupId fix (#113) by Dmitry Kornilov <maiden168@gmail.com>
  - 052502d5f8cef0b9f1f631ab83138c9c470c4beb add service provider configuration for classpath mode (#1... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 3ca0db0beb0eea791f1524b40ecb4d730181fd59 fix bundle resource packaging (#119) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - e82316a124e524e92ed14e1ae65aa56111a48bc7 fix for regression #104 (#127) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 01ee02ac803cbf5c78bbb39959b6b4c48f69a1a5 Jdk11 preparation, jakarta uptake, osgi and docs fixes (#... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 543b496a910356c03f0b919b80cd36902a837f91 fix parsing of invalid json with jsonvalue (#130) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 3b142eec04a61f92961afdbea5f3403a064148cd version in master should be 1.2 (#131) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 9ccc20a01ddbb8e70a74b4a9fe40eb7348c94551 Fixes #135: impl artifactId should be 'jakarta.json'  (#1... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 0fd6e98f62257e855b09a7348ab12438456d19f0 Use correct version of common annotations (#139) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 817010b5f66e965f19726a045b20adcb3ad76a1a Parent updated to 1.0.5. (#144) by Tomáš Kraus <tomas.kraus@oracle.com>
  - 77e8d242b56a2875d9a8ed4dd1d8c697834af7fe Update README.md by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - decc9d77d55191cb45559ea202ec5d9b63871533 Update README.md by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 5b44888209c53c0b9026c25e5537b99d77d86e92 Merge pull request #161 from leadpony/fix_jsonpointer by Werner Keil <werner.keil@gmx.net>
  - d9eff6686bb6cd9242b9dc0b3d4c83de7bd7fd26 Merge pull request #162 from leadpony/fix_javadoc11 by Werner Keil <werner.keil@gmx.net>
  - 89f768a53d7131d74d83db0d608d42f008113518 Boilerplate spec and Jakarta EE 8 related updates (#184) by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - f54831312f1850182e21c6184c4734120373fe8f Update README.txt by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 487e4a22228fdd326fc77699c762f9e73e8ea034 fix doc status in dev/release version (#192) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 1f18b590f8c3aa17390b4866cc51d23f8349f992 Update README.md by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 41d3e8e87046ae52381ad24337e5b2ebe40036be add speclicense, remove JSON-P occurences, update readme by Lukas Jungmann <lukas.jungmann@oracle.com>
  - fa68b5d83ed397dc9172a51d20a9524d7ea9b38b Set Trusty for the Travis to support OracleJDK8 by Jan Supol <jan.supol@oracle.com>
  - c8daac3b2a11ad7c7cbbdd7c56fcd919b0a224bf Fixed documentation error for Operation.REPLACE. (#172) by Jesper de Jong <jesperdj@users.noreply.github.com>
  - 8f748778e1ffd71be1f0b1f0e5ca8b5de48f5fcf Implemented hashCode() with caching for JsonArrayImpl, Js... by Jean-Philippe Gariépy <gawix@yahoo.com>
  - 4ec63ceadd19e8565896d3c21ed15d832cb90fb0 Fixes #146: Avoid using exceptions for flow control in Js... by Jason Hinch <46059987+jhinch-at-atlassian-com@users.noreply.github.com>
  - d4d0bf449c3ee7d127bc4e79243d81f9c0708956 fix cp holder (#201) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 5735bb62715578633d5721bf583b837506bbec73 Update overview.html (#208) by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 5cb006b440dfcd5108e27f984882d6bcde64e394 Merge pull request #214 from Cousjava/jakarta-rename by Werner Keil <werner.keil@gmx.net>
  - 7e261a696a361b3b16141ee8e7363da6c347179e Bug 169 : Operation path encoding as required by RFC6901 ... by dylanBehetre <dylan.behetre@capgemini.com>
  - ee6e2e6e5dbde08b15c24d59821bcc536ec46fd3 Fixed jabadoc build. (#217) by Tomáš Kraus <tomas.kraus@oracle.com>
  - 3174ac4077a1e2f14832ca79737db30d43ee387d Configured copyright and spotbugs plugin. (#222) by Tomáš Kraus <tomas.kraus@oracle.com>
  - eb28d33785ee278615294011027d4b8cf6614e36 Module java.json renamed to jakarta.json. (#223) by Tomáš Kraus <tomas.kraus@oracle.com>
  - 2360cabee260cb146769066b364a30d745efe175 Parent pom updated to 1.0.6 by Tomas Kraus <Tomas.Kraus@oracle.com>
  - 4e7977906d358730c2bc12522e8fad050a759b2a jakarta.json.jar will contain api and impl (#228) by jbescos <jorge.bescos.gascon@oracle.com>
  - dd38d6e9d0d101c7b3d0a5e8960331ef0fa9a628 Fix warning in API (#229) by jbescos <jorge.bescos.gascon@oracle.com>
  - 1d622ceb928407894488a4b1140266863dbd5566 Parent is not relatively reachable for this project (#231) by Piotrek Żygieło <pzygielo@users.noreply.github.com>
  - 57c8b5215d79995e77f9ff907844c4e65bd5545f Remove duplicated plugin, set up just above (#230) by Piotrek Żygieło <pzygielo@users.noreply.github.com>
  - 7186d73ef8050cbf58a380bc78aa5cb16b26f720 Merge pull request #224 from lmsurpre/master by Nathan Mittlestat <nmittles@us.ibm.com>
  - 3994ab23a5d122c0199565d5301080682f00222a Upgrade jakarta ws rs api to 3.0.0-M1 (#236) by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - b73a070c3b5cb6f09b405e933de857efdb1c1436 avoid costly service lookups from the default implementat... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 4d30288c1dca29b327076de12f06f007b0fbae5f #238: Update CONTRIBUTING.md for Specification Project's ... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 8a924b48fba919286a23ab828d1480e844d6dcaf Jsonp tck (#232) by jbescos <jorge.bescos.gascon@oracle.com>
  - 400b3260ff74fce6d0494045378686ca64091930 clean up api project build,require jdk11 as the base for ... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - db3113e9b8d66ee5038e4612e6c549f1f3699180 update travis by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 3b374281753eb4a469137fa6d4664ceb99638b1c require jdk11 for the build, by Lukas Jungmann <lukas.jungmann@oracle.com>
  - faad4ecb78a78913c32bdb9b8e5d1ca8cfafc82d drop integration with jaxrs 1x by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 15524e275d538bf1a6f00cf775121c44f55f59de Fix broken image in pdf spec, by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 36e1b43507413c92eb8b3e43138173148a730464 Integrate json-api 2.0.0 by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 5e834a30cec1f07d93a7971d7886e0716ef85520 change module name of the implementation to org.glassfish... by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 68293e807cfd15a450189c7add17de2f272adbf9 Integrate jakarta.servlet-api 5.0.0-M1 by Lukas Jungmann <lukas.jungmann@oracle.com>
  - e24d3faab99a29177653557257516d4d941ab943 update samples, by Lukas Jungmann <lukas.jungmann@oracle.com>
  - aab9d6ed1a95f438a0d5c8fddcbca7903669c047 allow build on jdk14 by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 5d7704d0573f2a107f9681d208bf3e9b490b084e  #211: Handle Collections and Maps containing JsonValues by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 44a6bd1f95e88388711cf2aee070ed8341f8f955 Signed-off-by: Lukas Jungmann <lukas.jungmann@oracle.com> by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 47ccfe65287a7cf84f1666a1d445b23b85ef5b24 fix spec vendor in manifest by Lukas Jungmann <lukas.jungmann@oracle.com>
  - dcef07f088197eb7f44829a3ccf4f6a9b99d29ff Fix project readme (#255) by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 6af0fad002efd25bfeda00f71c8c7aeeeb8a4f4a Update RI version of org.glassfish:jakarta.json to 2.0.0 by Eclipse JSON-P Bot <jsonp-bot@eclipse.org>
  - 0ddbbb642d0e8880a118aa4d4f6be4c249ca85e2 Update RI version of org.glassfish:jakarta.json to 2.0.1-... by Eclipse JSON-P Bot <jsonp-bot@eclipse.org>
  - abca44b7ab0d95f7aa5e994e79baef5cdcc8e000 Repare broken links from overview by Thibault Vallin <thibault.vallin@oracle.com>
  - 62c57bcdc78695d2fd7226791b23507ff933ae7a [api] bump project version, update build plugins by Lukas Jungmann <lukas.jungmann@oracle.com>
  - f0a26c5d83539ee624e9a30400af2198639747c3 Issue #240 - added support for "reject duplicate keys" co... by John T.E. Timm <johntimm@us.ibm.com>
  - 82f08a5d856634c77a6771b06ec05fb28bf61216 integrate latest dependencies, by Lukas Jungmann <lukas.jungmann@oracle.com>
  - dcf112f3992b2425f9397e428b8e16e00bca9272 fix cp headers by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 888975fc13b93d7ad69e3572de965d02e8dcf91e Integrate jsonp-api 2.0.1 by Lukas Jungmann <lukas.jungmann@oracle.com>
  - 97a55d55c7506a98a4cc9ccdc256f5f0242f04f9 CP year fix by Maxim Nesen <maxim.nesen@oracle.com>
  - 73014364fac9e67f981720f233ed86ec05aaff47 CP year fix by Maxim Nesen <maxim.nesen@oracle.com>
  - 35ed2f330c6d4fa2b3d50b020b5e84a76366af80 Update RI version of org.glassfish:jakarta.json to 2.0.1 by Eclipse JSON-P Bot <jsonp-bot@eclipse.org>

GitOrigin-RevId: 35ed2f330c6d4fa2b3d50b020b5e84a76366af80
Change-Id: If8677153dbbfdad97267f13ea4e9bf3eb4903964
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..998689f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+# Maven noise
+target/
+
+# OSX noise
+.DS_Store
+
+# IntelliJ Idea noise
+.idea
+*.iws
+*.ipr
+*.iml
+
+# Eclipse noise
+.settings/
+.classpath
+.project
+
+# NetBeans noise
+nbproject/
+
+# VS Code noise
+.vscode/
+
+# Java noise
+*.class
+*err_pid*.log
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..54994b3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,25 @@
+dist: bionic
+
+language: java
+
+jdk:
+  - openjdk11
+  - openjdk14
+
+cache:
+  directories:
+    - .autoconf
+    - $HOME/.m2
+
+install: true
+
+script:
+  - cd api
+  - mvn -U -C -Pstaging,oss-release -Dnon.final=true -Dgpg.skip=true clean verify
+  - cd ..
+  - mvn -U -C -Pstaging,oss-release,all -Dnon.final=true -Dgpg.skip=true clean verify
+  - cd tck
+  - mvn -U clean install
+  - cd ../impl-tck
+  - mvn -U clean test
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..c4527fe
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,74 @@
+[//]: # " Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. "
+[//]: # "  "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
+[//]: # " http://www.eclipse.org/legal/epl-2.0. "
+[//]: # "  "
+[//]: # " This Source Code may also be made available under the following Secondary "
+[//]: # " Licenses when the conditions for such availability set forth in the "
+[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
+[//]: # " version 2 with the GNU Classpath Exception, which is available at "
+[//]: # " https://www.gnu.org/software/classpath/license.html. "
+[//]: # "  "
+[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
+
+# Contributing to Jakarta JSON Processing
+
+Thanks for your interest in this project.
+
+## Project description
+
+Jakarta JSON Processing is a Java API to process (e.g. parse, generate,
+transform and query) JSON documents. It produces and consumes JSON in a
+streaming fashion (similar to StAX API for XML) and allows to build a Java
+object model for JSON using API classes (similar to DOM API for XML).
+
+* https://projects.eclipse.org/projects/ee4j.jsonp
+
+## Developer resources
+
+Information regarding source code management, builds, coding standards, and
+more.
+
+* https://projects.eclipse.org/projects/ee4j.jsonp/developer
+
+The project maintains the following source code repositories
+
+* https://github.com/eclipse-ee4j/jsonp
+
+## Eclipse Development Process
+
+This Eclipse Foundation open project is governed by the Eclipse Foundation
+Development Process and operates under the terms of the Eclipse IP Policy.
+
+The Jakarta EE Specification Committee has adopted the Jakarta EE Specification
+Process (JESP) in accordance with the Eclipse Foundation Specification Process
+v1.2 (EFSP) to ensure that the specification process is complied with by all
+Jakarta EE specification projects.
+
+* https://eclipse.org/projects/dev_process
+* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf
+* https://jakarta.ee/about/jesp/
+* https://www.eclipse.org/legal/efsp_non_assert.php
+
+## Eclipse Contributor Agreement
+
+Before your contribution can be accepted by the project team contributors must
+electronically sign the Eclipse Contributor Agreement (ECA).
+
+* http://www.eclipse.org/legal/ECA.php
+
+Commits that are provided by non-committers must have a Signed-off-by field in
+the footer indicating that the author is aware of the terms by which the
+contribution has been provided to the project. The non-committer must
+additionally have an Eclipse Foundation account and must have a signed Eclipse
+Contributor Agreement (ECA) on file.
+
+For more information, please see the Eclipse Committer Handbook:
+https://www.eclipse.org/projects/handbook/#resources-commit
+
+## Contact
+
+Contact the project developers via the project's "dev" list.
+
+* https://dev.eclipse.org/mailman/listinfo/jsonp-dev
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..5de3d1b
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,637 @@
+# Eclipse Public License - v 2.0
+
+        THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+        PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+        OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+    1. DEFINITIONS
+
+    "Contribution" means:
+
+      a) in the case of the initial Contributor, the initial content
+         Distributed under this Agreement, and
+
+      b) in the case of each subsequent Contributor: 
+         i) changes to the Program, and 
+         ii) additions to the Program;
+      where such changes and/or additions to the Program originate from
+      and are Distributed by that particular Contributor. A Contribution
+      "originates" from a Contributor if it was added to the Program by
+      such Contributor itself or anyone acting on such Contributor's behalf.
+      Contributions do not include changes or additions to the Program that
+      are not Modified Works.
+
+    "Contributor" means any person or entity that Distributes the Program.
+
+    "Licensed Patents" mean patent claims licensable by a Contributor which
+    are necessarily infringed by the use or sale of its Contribution alone
+    or when combined with the Program.
+
+    "Program" means the Contributions Distributed in accordance with this
+    Agreement.
+
+    "Recipient" means anyone who receives the Program under this Agreement
+    or any Secondary License (as applicable), including Contributors.
+
+    "Derivative Works" shall mean any work, whether in Source Code or other
+    form, that is based on (or derived from) the Program and for which the
+    editorial revisions, annotations, elaborations, or other modifications
+    represent, as a whole, an original work of authorship.
+
+    "Modified Works" shall mean any work in Source Code or other form that
+    results from an addition to, deletion from, or modification of the
+    contents of the Program, including, for purposes of clarity any new file
+    in Source Code form that contains any contents of the Program. Modified
+    Works shall not include works that contain only declarations,
+    interfaces, types, classes, structures, or files of the Program solely
+    in each case in order to link to, bind by name, or subclass the Program
+    or Modified Works thereof.
+
+    "Distribute" means the acts of a) distributing or b) making available
+    in any manner that enables the transfer of a copy.
+
+    "Source Code" means the form of a Program preferred for making
+    modifications, including but not limited to software source code,
+    documentation source, and configuration files.
+
+    "Secondary License" means either the GNU General Public License,
+    Version 2.0, or any later versions of that license, including any
+    exceptions or additional permissions as identified by the initial
+    Contributor.
+
+    2. GRANT OF RIGHTS
+
+      a) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free copyright
+      license to reproduce, prepare Derivative Works of, publicly display,
+      publicly perform, Distribute and sublicense the Contribution of such
+      Contributor, if any, and such Derivative Works.
+
+      b) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free patent
+      license under Licensed Patents to make, use, sell, offer to sell,
+      import and otherwise transfer the Contribution of such Contributor,
+      if any, in Source Code or other form. This patent license shall
+      apply to the combination of the Contribution and the Program if, at
+      the time the Contribution is added by the Contributor, such addition
+      of the Contribution causes such combination to be covered by the
+      Licensed Patents. The patent license shall not apply to any other
+      combinations which include the Contribution. No hardware per se is
+      licensed hereunder.
+
+      c) Recipient understands that although each Contributor grants the
+      licenses to its Contributions set forth herein, no assurances are
+      provided by any Contributor that the Program does not infringe the
+      patent or other intellectual property rights of any other entity.
+      Each Contributor disclaims any liability to Recipient for claims
+      brought by any other entity based on infringement of intellectual
+      property rights or otherwise. As a condition to exercising the
+      rights and licenses granted hereunder, each Recipient hereby
+      assumes sole responsibility to secure any other intellectual
+      property rights needed, if any. For example, if a third party
+      patent license is required to allow Recipient to Distribute the
+      Program, it is Recipient's responsibility to acquire that license
+      before distributing the Program.
+
+      d) Each Contributor represents that to its knowledge it has
+      sufficient copyright rights in its Contribution, if any, to grant
+      the copyright license set forth in this Agreement.
+
+      e) Notwithstanding the terms of any Secondary License, no
+      Contributor makes additional grants to any Recipient (other than
+      those set forth in this Agreement) as a result of such Recipient's
+      receipt of the Program under the terms of a Secondary License
+      (if permitted under the terms of Section 3).
+
+    3. REQUIREMENTS
+
+    3.1 If a Contributor Distributes the Program in any form, then:
+
+      a) the Program must also be made available as Source Code, in
+      accordance with section 3.2, and the Contributor must accompany
+      the Program with a statement that the Source Code for the Program
+      is available under this Agreement, and informs Recipients how to
+      obtain it in a reasonable manner on or through a medium customarily
+      used for software exchange; and
+
+      b) the Contributor may Distribute the Program under a license
+      different than this Agreement, provided that such license:
+         i) effectively disclaims on behalf of all other Contributors all
+         warranties and conditions, express and implied, including
+         warranties or conditions of title and non-infringement, and
+         implied warranties or conditions of merchantability and fitness
+         for a particular purpose;
+
+         ii) effectively excludes on behalf of all other Contributors all
+         liability for damages, including direct, indirect, special,
+         incidental and consequential damages, such as lost profits;
+
+         iii) does not attempt to limit or alter the recipients' rights
+         in the Source Code under section 3.2; and
+
+         iv) requires any subsequent distribution of the Program by any
+         party to be under a license that satisfies the requirements
+         of this section 3.
+
+    3.2 When the Program is Distributed as Source Code:
+
+      a) it must be made available under this Agreement, or if the
+      Program (i) is combined with other material in a separate file or
+      files made available under a Secondary License, and (ii) the initial
+      Contributor attached to the Source Code the notice described in
+      Exhibit A of this Agreement, then the Program may be made available
+      under the terms of such Secondary Licenses, and
+
+      b) a copy of this Agreement must be included with each copy of
+      the Program.
+
+    3.3 Contributors may not remove or alter any copyright, patent,
+    trademark, attribution notices, disclaimers of warranty, or limitations
+    of liability ("notices") contained within the Program from any copy of
+    the Program which they Distribute, provided that Contributors may add
+    their own appropriate notices.
+
+    4. COMMERCIAL DISTRIBUTION
+
+    Commercial distributors of software may accept certain responsibilities
+    with respect to end users, business partners and the like. While this
+    license is intended to facilitate the commercial use of the Program,
+    the Contributor who includes the Program in a commercial product
+    offering should do so in a manner which does not create potential
+    liability for other Contributors. Therefore, if a Contributor includes
+    the Program in a commercial product offering, such Contributor
+    ("Commercial Contributor") hereby agrees to defend and indemnify every
+    other Contributor ("Indemnified Contributor") against any losses,
+    damages and costs (collectively "Losses") arising from claims, lawsuits
+    and other legal actions brought by a third party against the Indemnified
+    Contributor to the extent caused by the acts or omissions of such
+    Commercial Contributor in connection with its distribution of the Program
+    in a commercial product offering. The obligations in this section do not
+    apply to any claims or Losses relating to any actual or alleged
+    intellectual property infringement. In order to qualify, an Indemnified
+    Contributor must: a) promptly notify the Commercial Contributor in
+    writing of such claim, and b) allow the Commercial Contributor to control,
+    and cooperate with the Commercial Contributor in, the defense and any
+    related settlement negotiations. The Indemnified Contributor may
+    participate in any such claim at its own expense.
+
+    For example, a Contributor might include the Program in a commercial
+    product offering, Product X. That Contributor is then a Commercial
+    Contributor. If that Commercial Contributor then makes performance
+    claims, or offers warranties related to Product X, those performance
+    claims and warranties are such Commercial Contributor's responsibility
+    alone. Under this section, the Commercial Contributor would have to
+    defend claims against the other Contributors related to those performance
+    claims and warranties, and if a court requires any other Contributor to
+    pay any damages as a result, the Commercial Contributor must pay
+    those damages.
+
+    5. NO WARRANTY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+    BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+    IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+    TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+    PURPOSE. Each Recipient is solely responsible for determining the
+    appropriateness of using and distributing the Program and assumes all
+    risks associated with its exercise of rights under this Agreement,
+    including but not limited to the risks and costs of program errors,
+    compliance with applicable laws, damage to or loss of data, programs
+    or equipment, and unavailability or interruption of operations.
+
+    6. DISCLAIMER OF LIABILITY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+    SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+    PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+    EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGES.
+
+    7. GENERAL
+
+    If any provision of this Agreement is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this Agreement, and without further
+    action by the parties hereto, such provision shall be reformed to the
+    minimum extent necessary to make such provision valid and enforceable.
+
+    If Recipient institutes patent litigation against any entity
+    (including a cross-claim or counterclaim in a lawsuit) alleging that the
+    Program itself (excluding combinations of the Program with other software
+    or hardware) infringes such Recipient's patent(s), then such Recipient's
+    rights granted under Section 2(b) shall terminate as of the date such
+    litigation is filed.
+
+    All Recipient's rights under this Agreement shall terminate if it
+    fails to comply with any of the material terms or conditions of this
+    Agreement and does not cure such failure in a reasonable period of
+    time after becoming aware of such noncompliance. If all Recipient's
+    rights under this Agreement terminate, Recipient agrees to cease use
+    and distribution of the Program as soon as reasonably practicable.
+    However, Recipient's obligations under this Agreement and any licenses
+    granted by Recipient relating to the Program shall continue and survive.
+
+    Everyone is permitted to copy and distribute copies of this Agreement,
+    but in order to avoid inconsistency the Agreement is copyrighted and
+    may only be modified in the following manner. The Agreement Steward
+    reserves the right to publish new versions (including revisions) of
+    this Agreement from time to time. No one other than the Agreement
+    Steward has the right to modify this Agreement. The Eclipse Foundation
+    is the initial Agreement Steward. The Eclipse Foundation may assign the
+    responsibility to serve as the Agreement Steward to a suitable separate
+    entity. Each new version of the Agreement will be given a distinguishing
+    version number. The Program (including Contributions) may always be
+    Distributed subject to the version of the Agreement under which it was
+    received. In addition, after a new version of the Agreement is published,
+    Contributor may elect to Distribute the Program (including its
+    Contributions) under the new version.
+
+    Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+    receives no rights or licenses to the intellectual property of any
+    Contributor under this Agreement, whether expressly, by implication,
+    estoppel or otherwise. All rights in the Program not expressly granted
+    under this Agreement are reserved. Nothing in this Agreement is intended
+    to be enforceable by any entity that is not a Contributor or Recipient.
+    No third-party beneficiary rights are created under this Agreement.
+
+    Exhibit A - Form of Secondary Licenses Notice
+
+    "This Source Code may also be made available under the following 
+    Secondary Licenses when the conditions for such availability set forth 
+    in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+    version(s), and exceptions or additional permissions here}."
+
+      Simply including a copy of this Agreement, including this Exhibit A
+      is not sufficient to license the Source Code under Secondary Licenses.
+
+      If it is not possible or desirable to put the notice in a particular
+      file, then You may include the notice in a location (such as a LICENSE
+      file in a relevant directory) where a recipient would be likely to
+      look for such a notice.
+
+      You may add additional accurate notices of copyright ownership.
+
+---
+
+##    The GNU General Public License (GPL) Version 2, June 1991
+
+    Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+    51 Franklin Street, Fifth Floor
+    Boston, MA 02110-1335
+    USA
+
+    Everyone is permitted to copy and distribute verbatim copies
+    of this license document, but changing it is not allowed.
+
+    Preamble
+
+    The licenses for most software are designed to take away your freedom to
+    share and change it. By contrast, the GNU General Public License is
+    intended to guarantee your freedom to share and change free software--to
+    make sure the software is free for all its users. This General Public
+    License applies to most of the Free Software Foundation's software and
+    to any other program whose authors commit to using it. (Some other Free
+    Software Foundation software is covered by the GNU Library General
+    Public License instead.) You can apply it to your programs, too.
+
+    When we speak of free software, we are referring to freedom, not price.
+    Our General Public Licenses are designed to make sure that you have the
+    freedom to distribute copies of free software (and charge for this
+    service if you wish), that you receive source code or can get it if you
+    want it, that you can change the software or use pieces of it in new
+    free programs; and that you know you can do these things.
+
+    To protect your rights, we need to make restrictions that forbid anyone
+    to deny you these rights or to ask you to surrender the rights. These
+    restrictions translate to certain responsibilities for you if you
+    distribute copies of the software, or if you modify it.
+
+    For example, if you distribute copies of such a program, whether gratis
+    or for a fee, you must give the recipients all the rights that you have.
+    You must make sure that they, too, receive or can get the source code.
+    And you must show them these terms so they know their rights.
+
+    We protect your rights with two steps: (1) copyright the software, and
+    (2) offer you this license which gives you legal permission to copy,
+    distribute and/or modify the software.
+
+    Also, for each author's protection and ours, we want to make certain
+    that everyone understands that there is no warranty for this free
+    software. If the software is modified by someone else and passed on, we
+    want its recipients to know that what they have is not the original, so
+    that any problems introduced by others will not reflect on the original
+    authors' reputations.
+
+    Finally, any free program is threatened constantly by software patents.
+    We wish to avoid the danger that redistributors of a free program will
+    individually obtain patent licenses, in effect making the program
+    proprietary. To prevent this, we have made it clear that any patent must
+    be licensed for everyone's free use or not licensed at all.
+
+    The precise terms and conditions for copying, distribution and
+    modification follow.
+
+    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License applies to any program or other work which contains a
+    notice placed by the copyright holder saying it may be distributed under
+    the terms of this General Public License. The "Program", below, refers
+    to any such program or work, and a "work based on the Program" means
+    either the Program or any derivative work under copyright law: that is
+    to say, a work containing the Program or a portion of it, either
+    verbatim or with modifications and/or translated into another language.
+    (Hereinafter, translation is included without limitation in the term
+    "modification".) Each licensee is addressed as "you".
+
+    Activities other than copying, distribution and modification are not
+    covered by this License; they are outside its scope. The act of running
+    the Program is not restricted, and the output from the Program is
+    covered only if its contents constitute a work based on the Program
+    (independent of having been made by running the Program). Whether that
+    is true depends on what the Program does.
+
+    1. You may copy and distribute verbatim copies of the Program's source
+    code as you receive it, in any medium, provided that you conspicuously
+    and appropriately publish on each copy an appropriate copyright notice
+    and disclaimer of warranty; keep intact all the notices that refer to
+    this License and to the absence of any warranty; and give any other
+    recipients of the Program a copy of this License along with the Program.
+
+    You may charge a fee for the physical act of transferring a copy, and
+    you may at your option offer warranty protection in exchange for a fee.
+
+    2. You may modify your copy or copies of the Program or any portion of
+    it, thus forming a work based on the Program, and copy and distribute
+    such modifications or work under the terms of Section 1 above, provided
+    that you also meet all of these conditions:
+
+        a) You must cause the modified files to carry prominent notices
+        stating that you changed the files and the date of any change.
+
+        b) You must cause any work that you distribute or publish, that in
+        whole or in part contains or is derived from the Program or any part
+        thereof, to be licensed as a whole at no charge to all third parties
+        under the terms of this License.
+
+        c) If the modified program normally reads commands interactively
+        when run, you must cause it, when started running for such
+        interactive use in the most ordinary way, to print or display an
+        announcement including an appropriate copyright notice and a notice
+        that there is no warranty (or else, saying that you provide a
+        warranty) and that users may redistribute the program under these
+        conditions, and telling the user how to view a copy of this License.
+        (Exception: if the Program itself is interactive but does not
+        normally print such an announcement, your work based on the Program
+        is not required to print an announcement.)
+
+    These requirements apply to the modified work as a whole. If
+    identifiable sections of that work are not derived from the Program, and
+    can be reasonably considered independent and separate works in
+    themselves, then this License, and its terms, do not apply to those
+    sections when you distribute them as separate works. But when you
+    distribute the same sections as part of a whole which is a work based on
+    the Program, the distribution of the whole must be on the terms of this
+    License, whose permissions for other licensees extend to the entire
+    whole, and thus to each and every part regardless of who wrote it.
+
+    Thus, it is not the intent of this section to claim rights or contest
+    your rights to work written entirely by you; rather, the intent is to
+    exercise the right to control the distribution of derivative or
+    collective works based on the Program.
+
+    In addition, mere aggregation of another work not based on the Program
+    with the Program (or with a work based on the Program) on a volume of a
+    storage or distribution medium does not bring the other work under the
+    scope of this License.
+
+    3. You may copy and distribute the Program (or a work based on it,
+    under Section 2) in object code or executable form under the terms of
+    Sections 1 and 2 above provided that you also do one of the following:
+
+        a) Accompany it with the complete corresponding machine-readable
+        source code, which must be distributed under the terms of Sections 1
+        and 2 above on a medium customarily used for software interchange; or,
+
+        b) Accompany it with a written offer, valid for at least three
+        years, to give any third party, for a charge no more than your cost
+        of physically performing source distribution, a complete
+        machine-readable copy of the corresponding source code, to be
+        distributed under the terms of Sections 1 and 2 above on a medium
+        customarily used for software interchange; or,
+
+        c) Accompany it with the information you received as to the offer to
+        distribute corresponding source code. (This alternative is allowed
+        only for noncommercial distribution and only if you received the
+        program in object code or executable form with such an offer, in
+        accord with Subsection b above.)
+
+    The source code for a work means the preferred form of the work for
+    making modifications to it. For an executable work, complete source code
+    means all the source code for all modules it contains, plus any
+    associated interface definition files, plus the scripts used to control
+    compilation and installation of the executable. However, as a special
+    exception, the source code distributed need not include anything that is
+    normally distributed (in either source or binary form) with the major
+    components (compiler, kernel, and so on) of the operating system on
+    which the executable runs, unless that component itself accompanies the
+    executable.
+
+    If distribution of executable or object code is made by offering access
+    to copy from a designated place, then offering equivalent access to copy
+    the source code from the same place counts as distribution of the source
+    code, even though third parties are not compelled to copy the source
+    along with the object code.
+
+    4. You may not copy, modify, sublicense, or distribute the Program
+    except as expressly provided under this License. Any attempt otherwise
+    to copy, modify, sublicense or distribute the Program is void, and will
+    automatically terminate your rights under this License. However, parties
+    who have received copies, or rights, from you under this License will
+    not have their licenses terminated so long as such parties remain in
+    full compliance.
+
+    5. You are not required to accept this License, since you have not
+    signed it. However, nothing else grants you permission to modify or
+    distribute the Program or its derivative works. These actions are
+    prohibited by law if you do not accept this License. Therefore, by
+    modifying or distributing the Program (or any work based on the
+    Program), you indicate your acceptance of this License to do so, and all
+    its terms and conditions for copying, distributing or modifying the
+    Program or works based on it.
+
+    6. Each time you redistribute the Program (or any work based on the
+    Program), the recipient automatically receives a license from the
+    original licensor to copy, distribute or modify the Program subject to
+    these terms and conditions. You may not impose any further restrictions
+    on the recipients' exercise of the rights granted herein. You are not
+    responsible for enforcing compliance by third parties to this License.
+
+    7. If, as a consequence of a court judgment or allegation of patent
+    infringement or for any other reason (not limited to patent issues),
+    conditions are imposed on you (whether by court order, agreement or
+    otherwise) that contradict the conditions of this License, they do not
+    excuse you from the conditions of this License. If you cannot distribute
+    so as to satisfy simultaneously your obligations under this License and
+    any other pertinent obligations, then as a consequence you may not
+    distribute the Program at all. For example, if a patent license would
+    not permit royalty-free redistribution of the Program by all those who
+    receive copies directly or indirectly through you, then the only way you
+    could satisfy both it and this License would be to refrain entirely from
+    distribution of the Program.
+
+    If any portion of this section is held invalid or unenforceable under
+    any particular circumstance, the balance of the section is intended to
+    apply and the section as a whole is intended to apply in other
+    circumstances.
+
+    It is not the purpose of this section to induce you to infringe any
+    patents or other property right claims or to contest validity of any
+    such claims; this section has the sole purpose of protecting the
+    integrity of the free software distribution system, which is implemented
+    by public license practices. Many people have made generous
+    contributions to the wide range of software distributed through that
+    system in reliance on consistent application of that system; it is up to
+    the author/donor to decide if he or she is willing to distribute
+    software through any other system and a licensee cannot impose that choice.
+
+    This section is intended to make thoroughly clear what is believed to be
+    a consequence of the rest of this License.
+
+    8. If the distribution and/or use of the Program is restricted in
+    certain countries either by patents or by copyrighted interfaces, the
+    original copyright holder who places the Program under this License may
+    add an explicit geographical distribution limitation excluding those
+    countries, so that distribution is permitted only in or among countries
+    not thus excluded. In such case, this License incorporates the
+    limitation as if written in the body of this License.
+
+    9. The Free Software Foundation may publish revised and/or new
+    versions of the General Public License from time to time. Such new
+    versions will be similar in spirit to the present version, but may
+    differ in detail to address new problems or concerns.
+
+    Each version is given a distinguishing version number. If the Program
+    specifies a version number of this License which applies to it and "any
+    later version", you have the option of following the terms and
+    conditions either of that version or of any later version published by
+    the Free Software Foundation. If the Program does not specify a version
+    number of this License, you may choose any version ever published by the
+    Free Software Foundation.
+
+    10. If you wish to incorporate parts of the Program into other free
+    programs whose distribution conditions are different, write to the
+    author to ask for permission. For software which is copyrighted by the
+    Free Software Foundation, write to the Free Software Foundation; we
+    sometimes make exceptions for this. Our decision will be guided by the
+    two goals of preserving the free status of all derivatives of our free
+    software and of promoting the sharing and reuse of software generally.
+
+    NO WARRANTY
+
+    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+    WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+    EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+    OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+    EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+    YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+    NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+    AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+    END OF TERMS AND CONDITIONS
+
+    How to Apply These Terms to Your New Programs
+
+    If you develop a new program, and you want it to be of the greatest
+    possible use to the public, the best way to achieve this is to make it
+    free software which everyone can redistribute and change under these terms.
+
+    To do so, attach the following notices to the program. It is safest to
+    attach them to the start of each source file to most effectively convey
+    the exclusion of warranty; and each file should have at least the
+    "copyright" line and a pointer to where the full notice is found.
+
+        One line to give the program's name and a brief idea of what it does.
+        Copyright (C) <year> <name of author>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with this program; if not, write to the Free Software
+        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+    Also add information on how to contact you by electronic and paper mail.
+
+    If the program is interactive, make it output a short notice like this
+    when it starts in an interactive mode:
+
+        Gnomovision version 69, Copyright (C) year name of author
+        Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type
+        `show w'. This is free software, and you are welcome to redistribute
+        it under certain conditions; type `show c' for details.
+
+    The hypothetical commands `show w' and `show c' should show the
+    appropriate parts of the General Public License. Of course, the commands
+    you use may be called something other than `show w' and `show c'; they
+    could even be mouse-clicks or menu items--whatever suits your program.
+
+    You should also get your employer (if you work as a programmer) or your
+    school, if any, to sign a "copyright disclaimer" for the program, if
+    necessary. Here is a sample; alter the names:
+
+        Yoyodyne, Inc., hereby disclaims all copyright interest in the
+        program `Gnomovision' (which makes passes at compilers) written by
+        James Hacker.
+
+        signature of Ty Coon, 1 April 1989
+        Ty Coon, President of Vice
+
+    This General Public License does not permit incorporating your program
+    into proprietary programs. If your program is a subroutine library, you
+    may consider it more useful to permit linking proprietary applications
+    with the library. If this is what you want to do, use the GNU Library
+    General Public License instead of this License.
+
+---
+
+## CLASSPATH EXCEPTION
+
+    Linking this library statically or dynamically with other modules is
+    making a combined work based on this library.  Thus, the terms and
+    conditions of the GNU General Public License version 2 cover the whole
+    combination.
+
+    As a special exception, the copyright holders of this library give you
+    permission to link this library with independent modules to produce an
+    executable, regardless of the license terms of these independent
+    modules, and to copy and distribute the resulting executable under
+    terms of your choice, provided that you also meet, for each linked
+    independent module, the terms and conditions of the license of that
+    module.  An independent module is a module which is not derived from or
+    based on this library.  If you modify this library, you may extend this
+    exception to your version of the library, but you are not obligated to
+    do so.  If you do not wish to do so, delete this exception statement
+    from your version.
diff --git a/NOTICE.md b/NOTICE.md
new file mode 100644
index 0000000..18f0422
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,86 @@
+[//]: # " Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. "
+[//]: # "  "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
+[//]: # " http://www.eclipse.org/legal/epl-2.0. "
+[//]: # "  "
+[//]: # " This Source Code may also be made available under the following Secondary "
+[//]: # " Licenses when the conditions for such availability set forth in the "
+[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
+[//]: # " version 2 with the GNU Classpath Exception, which is available at "
+[//]: # " https://www.gnu.org/software/classpath/license.html. "
+[//]: # "  "
+[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
+
+# Notices for Jakarta JSON Processing
+
+This content is produced and maintained by the Jakarta JSON Processing project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.jsonp
+
+## Trademarks
+
+ Jakarta JSON Processing is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License v2.0 w/Classpath exception which is available at
+https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jsonp
+
+## Third-party Content
+
+This project leverages the following third party content.
+
+javax.ws.rs-api:2.0.1 (2.0.1)
+
+* License: (CDDL-1.1 OR GPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0) AND
+   Apache-2.0
+* Project: https://github.com/jax-rs/api
+* Source: https://github.com/jax-rs/api
+
+javax.ws.rs:jsr311-api:jar:1.1.1 (1.1.1)
+
+* License: CDDL-1.0 AND Apache-2.0
+* Project: https://github.com/jax-rs/api
+* Source:
+   http://search.maven.org/remotecontent?filepath=javax/ws/rs/jsr311-api/1.1.1/jsr311-api-1.1.1-sources.jar
+
+javax:javaee-web-api:jar:7.0 (7.0)
+
+* License: (CDDL-1.0 OR GPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0) AND
+   (EPL-1.0 OR BSD-3-Clause) AND Apache-2.0 AND LicenseRef-Public Domain
+* Project: https://javaee.github.io
+* Source:
+   http://search.maven.org/remotecontent?filepath=javax/javaee-web-api/7.0/javaee-web-api-7.0-sources.jar
+
+JUnit (4.12)
+
+* License: Eclipse Public License
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..52567e1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+[//]: # " Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved. "
+[//]: # "  "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
+[//]: # " http://www.eclipse.org/legal/epl-2.0. "
+[//]: # "  "
+[//]: # " This Source Code may also be made available under the following Secondary "
+[//]: # " Licenses when the conditions for such availability set forth in the "
+[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
+[//]: # " version 2 with the GNU Classpath Exception, which is available at "
+[//]: # " https://www.gnu.org/software/classpath/license.html. "
+[//]: # "  "
+[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
+
+[![Build Status](https://travis-ci.org/eclipse-ee4j/jsonp.svg?branch=master)](https://travis-ci.org/eclipse-ee4j/jsonp)
+
+# Jakarta JSON Processing
+
+Jakarta JSON Processing provides portable APIs to parse, generate, transform, and query JSON documents.
+This project contains Jakarta JSON Processing specification, API and a compatible implementation.
+
+## Build
+
+Use the following command:
+```bash
+mvn -U -C clean install
+```
+
+## License
+
+* Most of the Jakarta JSON Processing project source code is licensed
+under the [Eclipse Public License (EPL) v2.0](https://projects.eclipse.org/license/epl-2.0)
+and [GNU General Public License (GPL) v2 with Classpath Exception](https://www.gnu.org/software/classpath/license.html);
+see the license information at the top of each source file.
+* The source code for the demo programs is licensed
+under the [Eclipse Distribution License (EDL) v1.0.](https://www.eclipse.org/org/documents/edl-v10.php).
+* The binary jar files published to the Maven repository are licensed
+under the same licenses as the corresponding source code;
+see the file `META-INF/LICENSE.txt` in each jar file.
+
+You’ll find the text of the licenses in the workspace in various `LICENSE.txt` or `LICENSE.md` files.
+Don’t let the presence of these license files in the workspace confuse you into thinking
+that they apply to all files in the workspace.
+
+You should always read the license file included with every download, and read
+the license text included in every source file.
+
+## Links
+
+- [Jakarta JSON Processing official web site](https://eclipse-ee4j.github.io/jsonp)
+- [Jakarta JSON Processing @ Eclipse](https://projects.eclipse.org/projects/ee4j.jsonp)
+- [README.txt](https://github.com/eclipse-ee4j/jsonp/blob/master/bundles/ri/src/main/resources/README.txt)
+
+## Contributing
+
+We use [contribution policy](CONTRIBUTING.md), which means we can only accept contributions under
+the terms of [Eclipse Contributor Agreement](http://www.eclipse.org/legal/ECA.php).
diff --git a/api/etc/config/copyright-exclude b/api/etc/config/copyright-exclude
new file mode 100644
index 0000000..e7f6f88
--- /dev/null
+++ b/api/etc/config/copyright-exclude
@@ -0,0 +1,8 @@
+.json
+.md
+speclicense.html
+MANIFEST.MF
+/META-INF/services/
+/etc/config/copyright-exclude
+/etc/config/copyright.txt
+/bundles/ri/src/main/resources/README.txt
diff --git a/api/etc/config/copyright.txt b/api/etc/config/copyright.txt
new file mode 100644
index 0000000..9c347b6
--- /dev/null
+++ b/api/etc/config/copyright.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) YYYY Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
diff --git a/api/etc/config/exclude.xml b/api/etc/config/exclude.xml
new file mode 100644
index 0000000..55ca904
--- /dev/null
+++ b/api/etc/config/exclude.xml
@@ -0,0 +1,14 @@
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<FindBugsFilter>
+</FindBugsFilter>
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000..e2a66bb
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.6</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>jakarta.json</groupId>
+    <artifactId>jakarta.json-api</artifactId>
+
+    <version>2.0.1-SNAPSHOT</version>
+    <name>Jakarta JSON Processing API</name>
+    <description>Jakarta JSON Processing defines a Java(R) based framework for parsing, generating, transforming, and querying JSON documents.</description>
+    <url>https://github.com/eclipse-ee4j/jsonp</url>
+
+    <scm>
+        <connection>scm:git:git://github.com/eclipse-ee4j/jsonp.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jsonp.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jsonp</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <licenses>
+        <license>
+            <name>Eclipse Public License 2.0</name>
+            <url>https://projects.eclipse.org/license/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GNU General Public License, version 2 with the GNU Classpath Exception</name>
+            <url>https://projects.eclipse.org/license/secondary-gpl-2.0-cp</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <developers>
+        <developer>
+            <id>m0mus</id>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>project lead</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>lukasj</id>
+            <name>Lukas Jungmann</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>dev lead</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <properties>
+        <config.dir>${project.root.location}/etc/config</config.dir>
+        <copyright.exclude>${config.dir}/copyright-exclude</copyright.exclude>
+        <copyright.templatefile>${config.dir}/copyright.txt</copyright.templatefile>
+        <copyright.ignoreyear>false</copyright.ignoreyear>
+        <copyright.scmonly>true</copyright.scmonly>
+        <copyright.update>false</copyright.update>
+        <spotbugs.skip>false</spotbugs.skip>
+        <spotbugs.threshold>Low</spotbugs.threshold>
+        <spotbugs.version>4.2.2</spotbugs.version>
+
+        <non.final>false</non.final>
+        <extension.name>jakarta.json</extension.name>
+        <spec.version>2.0</spec.version>
+        <legal.doc.source>${project.basedir}/..</legal.doc.source>
+        <vendor.name>Eclipse Foundation</vendor.name>
+    </properties>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.commonjava.maven.plugins</groupId>
+                    <artifactId>directory-maven-plugin</artifactId>
+                    <version>0.3.1</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>buildnumber-maven-plugin</artifactId>
+                    <version>1.4</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.copyright</groupId>
+                    <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                    <version>2.4</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.build</groupId>
+                    <artifactId>spec-version-maven-plugin</artifactId>
+                    <version>2.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>5.1.1</version>
+                    <configuration>
+                        <instructions>
+                            <_noextraheaders>true</_noextraheaders>
+                        </instructions>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>3.2.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-enforcer-plugin</artifactId>
+                    <version>3.0.0-M3</version>
+                </plugin>
+                <plugin>
+                    <groupId>com.github.spotbugs</groupId>
+                    <artifactId>spotbugs-maven-plugin</artifactId>
+                    <version>${spotbugs.version}</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>enforce-maven</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireJavaVersion>
+                                    <version>[11,)</version>
+                                </requireJavaVersion>
+                                <requireMavenVersion>
+                                    <version>[3.6.0,)</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.commonjava.maven.plugins</groupId>
+                <artifactId>directory-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>find-project-root</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>highest-basedir</goal>
+                        </goals>
+                        <configuration>
+                            <property>project.root.location</property>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <!-- Requires validate target to initialize copyright.config.dir properly -->
+                <!-- e.g. mvn validate glassfish-copyright:repair -->
+                <groupId>org.glassfish.copyright</groupId>
+                <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                <configuration>
+                    <excludeFile>${copyright.exclude}</excludeFile>
+                    <scmOnly>${copyright.scmonly}</scmOnly>
+                    <update>${copyright.update}</update>
+                    <ignoreYear>${copyright.ignoreyear}</ignoreYear>
+                    <quiet>false</quiet>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>buildnumber-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>validate</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>create</goal>
+                        </goals>
+                        <configuration>
+                            <getRevisionOnlyOnce>true</getRevisionOnlyOnce>
+                            <shortRevisionLength>7</shortRevisionLength>
+                            <revisionOnScmFailure>false</revisionOnScmFailure>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-legal-resource</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>add-resource</goal>
+                        </goals>
+                        <configuration>
+                            <resources>
+                                <resource>
+                                    <directory>${legal.doc.source}</directory>
+                                    <includes>
+                                        <include>NOTICE.md</include>
+                                        <include>LICENSE.md</include>
+                                    </includes>
+                                    <targetPath>META-INF</targetPath>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                     <execution>
+                        <id>currentyear-property</id>
+                        <goals>
+                            <goal>timestamp-property</goal>
+                        </goals>
+                        <phase>validate</phase>
+                        <configuration>
+                            <name>current.year</name>
+                            <locale>en,US</locale>
+                            <pattern>yyyy</pattern>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <release>9</release>
+                    <compilerArgs>
+                        <arg>-Xlint:all</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>base-compile</id>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <release>8</release>
+                            <excludes>
+                                <exclude>module-info.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.build</groupId>
+                <artifactId>spec-version-maven-plugin</artifactId>
+                <configuration>
+                    <spec>
+                        <nonFinal>${non.final}</nonFinal>
+                        <jarType>api</jarType>
+                        <specVersion>${spec.version}</specVersion>
+                        <specImplVersion>${project.version}</specImplVersion>
+                        <apiPackage>${extension.name}</apiPackage>
+                    </spec>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>set-spec-properties</goal>
+                            <!-- TODO:
+                            glassfish-spec-version-maven-plugin needs to be updated
+                            in order to check 'jakarta.' prefixed values in manifest entries
+                            -->
+                            <!--<goal>check-module</goal>-->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Version>${spec.bundle.version}</Bundle-Version>
+                        <Bundle-SymbolicName>${spec.bundle.symbolic-name}</Bundle-SymbolicName>
+                        <Extension-Name>${spec.extension.name}</Extension-Name>
+                        <Implementation-Version>${spec.implementation.version}</Implementation-Version>
+                        <Specification-Version>${spec.specification.version}</Specification-Version>
+                        <Bundle-Description>Jakarta JSON Processing API ${spec.version}</Bundle-Description>
+                        <Specification-Vendor>${vendor.name}</Specification-Vendor>
+                        <Implementation-Build-Id>${buildNumber}</Implementation-Build-Id>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultEntries>false</addDefaultEntries>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                        </manifest>
+                        <manifestEntries>
+                            <Implementation-Build-Id>${buildNumber}</Implementation-Build-Id>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultEntries>false</addDefaultEntries>
+                        </manifest>
+                    </archive>
+                    <release>11</release>
+                    <notimestamp>true</notimestamp>
+                    <docfilessubdirs>true</docfilessubdirs>
+                    <description>JSON Processing API documentation</description>
+                    <doctitle>JSON Processing API documentation</doctitle>
+                    <windowtitle>JSON Processing API documentation</windowtitle>
+                    <header><![CDATA[<br>JSON Processing API v${project.version}]]></header>
+                    <bottom><![CDATA[
+Comments to: <a href="mailto:jsonp-dev@eclipse.org">jsonp-dev@eclipse.org</a>.<br>
+Copyright &#169; 2019, ${current.year} Eclipse Foundation. All rights reserved.<br>
+Use is subject to <a href="{@docRoot}/doc-files/speclicense.html" target="_top">license terms</a>.]]>
+                    </bottom>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <configuration>
+                    <skip>${spotbugs.skip}</skip>
+                    <threshold>${spotbugs.threshold}</threshold>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                    <fork>true</fork>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    
+</project>
diff --git a/api/src/main/java/jakarta/json/EmptyArray.java b/api/src/main/java/jakarta/json/EmptyArray.java
new file mode 100644
index 0000000..9d48dc2
--- /dev/null
+++ b/api/src/main/java/jakarta/json/EmptyArray.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.Collections;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * Private implementation of immutable {@link JsonArray}.
+ *
+ * @author Lukas Jungmann
+ */
+final class EmptyArray extends AbstractList<JsonValue> implements JsonArray, Serializable, RandomAccess {
+
+    private static final long serialVersionUID = 7295439472061642859L;
+
+    @Override
+    public JsonValue get(int index) {
+        throw new IndexOutOfBoundsException("Index: " + index);
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public JsonObject getJsonObject(int index) {
+        return (JsonObject) get(index);
+    }
+
+    @Override
+    public JsonArray getJsonArray(int index) {
+        return (JsonArray) get(index);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(int index) {
+        return (JsonNumber) get(index);
+    }
+
+    @Override
+    public JsonString getJsonString(int index) {
+        return (JsonString) get(index);
+    }
+
+    @Override
+    public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public String getString(int index) {
+        return getJsonString(index).getString();
+    }
+
+    @Override
+    public String getString(int index, String defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public int getInt(int index) {
+        return getJsonNumber(index).intValue();
+    }
+
+    @Override
+    public int getInt(int index, int defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public boolean getBoolean(int index) {
+        return get(index) == JsonValue.TRUE;
+    }
+
+    @Override
+    public boolean getBoolean(int index, boolean defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public boolean isNull(int index) {
+        return get(index) == JsonValue.NULL;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.ARRAY;
+    }
+
+    // Preserves singleton property
+    private Object readResolve() {
+        return JsonValue.EMPTY_JSON_ARRAY;
+    }
+}
diff --git a/api/src/main/java/jakarta/json/EmptyObject.java b/api/src/main/java/jakarta/json/EmptyObject.java
new file mode 100644
index 0000000..fa3254d
--- /dev/null
+++ b/api/src/main/java/jakarta/json/EmptyObject.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Private implementation of immutable {@link JsonObject}.
+ *
+ * @author Lukas Jungmann
+ */
+final class EmptyObject extends AbstractMap<String, JsonValue> implements JsonObject, Serializable {
+
+    private static final long serialVersionUID = -1461653546889072583L;
+
+    @Override
+    public Set<Entry<String, JsonValue>> entrySet() {
+        return Collections.<Entry<String, JsonValue>>emptySet();
+    }
+
+    @Override
+    public JsonArray getJsonArray(String name) {
+        return (JsonArray) get(name);
+    }
+
+    @Override
+    public JsonObject getJsonObject(String name) {
+        return (JsonObject) get(name);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(String name) {
+        return (JsonNumber) get(name);
+    }
+
+    @Override
+    public JsonString getJsonString(String name) {
+        return (JsonString) get(name);
+    }
+
+    @Override
+    public String getString(String name) {
+        return getJsonString(name).getString();
+    }
+
+    @Override
+    public String getString(String name, String defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public int getInt(String name) {
+        return getJsonNumber(name).intValue();
+    }
+
+    @Override
+    public int getInt(String name, int defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public boolean getBoolean(String name) {
+        throw new NullPointerException();
+    }
+
+    @Override
+    public boolean getBoolean(String name, boolean defaultValue) {
+        return defaultValue;
+    }
+
+    @Override
+    public boolean isNull(String name) {
+        throw new NullPointerException();
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.OBJECT;
+    }
+
+    // Preserves singleton property
+    private Object readResolve() {
+        return JsonValue.EMPTY_JSON_OBJECT;
+    }
+}
diff --git a/api/src/main/java/jakarta/json/Json.java b/api/src/main/java/jakarta/json/Json.java
new file mode 100644
index 0000000..cdbc7ee
--- /dev/null
+++ b/api/src/main/java/jakarta/json/Json.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+
+/**
+ * Factory class for creating JSON processing objects.
+ * This class provides the most commonly used methods for creating these
+ * objects and their corresponding factories. The factory classes provide
+ * all the various ways to create these objects.
+ *
+ * <p>
+ * The methods in this class locate a provider instance using the method
+ * {@link JsonProvider#provider()}. This class uses the provider instance
+ * to create JSON processing objects.
+ *
+ * <p>
+ * The following example shows how to create a JSON parser to parse
+ * an empty array:
+ * <pre>
+ * <code>
+ * StringReader reader = new StringReader("[]");
+ * JsonParser parser = Json.createParser(reader);
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public final class Json {
+
+    private Json() {
+    }
+
+    /**
+     * Creates a JSON parser from a character stream.
+     *
+     * @param reader i/o reader from which JSON is to be read
+     * @return a JSON parser
+     */
+    public static JsonParser createParser(Reader reader) {
+        return JsonProvider.provider().createParser(reader);
+    }
+
+    /**
+     * Creates a JSON parser from a byte stream.
+     * The character encoding of the stream is determined as specified in
+     * <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+     *
+     * @param in i/o stream from which JSON is to be read
+     * @throws JsonException if encoding cannot be determined
+     *         or i/o error (IOException would be cause of JsonException)
+     * @return a JSON parser
+     */
+    public static JsonParser createParser(InputStream in) {
+        return JsonProvider.provider().createParser(in);
+    }
+
+    /**
+     * Creates a JSON generator for writing JSON to a character stream.
+     *
+     * @param writer a i/o writer to which JSON is written
+     * @return a JSON generator
+     */
+    public static JsonGenerator createGenerator(Writer writer) {
+        return JsonProvider.provider().createGenerator(writer);
+    }
+
+    /**
+     * Creates a JSON generator for writing JSON to a byte stream.
+     *
+     * @param out i/o stream to which JSON is written
+     * @return a JSON generator
+     */
+    public static JsonGenerator createGenerator(OutputStream out) {
+        return JsonProvider.provider().createGenerator(out);
+    }
+
+    /**
+     * Creates a parser factory for creating {@link JsonParser} objects.
+     *
+     * @return JSON parser factory.
+     *
+    public static JsonParserFactory createParserFactory() {
+        return JsonProvider.provider().createParserFactory();
+    }
+     */
+
+    /**
+     * Creates a parser factory for creating {@link JsonParser} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON parsers. The map may be empty or null
+     * @return JSON parser factory
+     */
+    public static JsonParserFactory createParserFactory(Map<String, ?> config) {
+        return JsonProvider.provider().createParserFactory(config);
+    }
+
+    /**
+     * Creates a generator factory for creating {@link JsonGenerator} objects.
+     *
+     * @return JSON generator factory
+     *
+    public static JsonGeneratorFactory createGeneratorFactory() {
+        return JsonProvider.provider().createGeneratorFactory();
+    }
+    */
+
+    /**
+     * Creates a generator factory for creating {@link JsonGenerator} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON generators. The map may be empty or null
+     * @return JSON generator factory
+     */
+    public static JsonGeneratorFactory createGeneratorFactory(
+            Map<String, ?> config) {
+        return JsonProvider.provider().createGeneratorFactory(config);
+    }
+
+    /**
+     * Creates a JSON writer to write a
+     * JSON {@link JsonObject object} or {@link JsonArray array}
+     * structure to the specified character stream.
+     *
+     * @param writer to which JSON object or array is written
+     * @return a JSON writer
+     */
+    public static JsonWriter createWriter(Writer writer) {
+        return JsonProvider.provider().createWriter(writer);
+    }
+
+    /**
+     * Creates a JSON writer to write a
+     * JSON {@link JsonObject object} or {@link JsonArray array}
+     * structure to the specified byte stream. Characters written to
+     * the stream are encoded into bytes using UTF-8 encoding.
+     *
+     * @param out to which JSON object or array is written
+     * @return a JSON writer
+     */
+    public static JsonWriter createWriter(OutputStream out) {
+        return JsonProvider.provider().createWriter(out);
+    }
+
+    /**
+     * Creates a JSON reader from a character stream.
+     *
+     * @param reader a reader from which JSON is to be read
+     * @return a JSON reader
+     */
+    public static JsonReader createReader(Reader reader) {
+        return JsonProvider.provider().createReader(reader);
+    }
+
+    /**
+     * Creates a JSON reader from a byte stream. The character encoding of
+     * the stream is determined as described in
+     * <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+     *
+     * @param in a byte stream from which JSON is to be read
+     * @return a JSON reader
+     */
+    public static JsonReader createReader(InputStream in) {
+        return JsonProvider.provider().createReader(in);
+    }
+
+    /**
+     * Creates a reader factory for creating {@link JsonReader} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON readers. The map may be empty or null
+     * @return a JSON reader factory
+     */
+    public static JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+        return JsonProvider.provider().createReaderFactory(config);
+    }
+
+    /**
+     * Creates a writer factory for creating {@link JsonWriter} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON writers. The map may be empty or null
+     * @return a JSON writer factory
+     */
+    public static JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+        return JsonProvider.provider().createWriterFactory(config);
+    }
+
+    /**
+     * Creates a JSON array builder
+     *
+     * @return a JSON array builder
+     */
+    public static JsonArrayBuilder createArrayBuilder() {
+        return JsonProvider.provider().createArrayBuilder();
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with the specified array
+     *
+     * @param array the initial array in the builder
+     * @return a JSON array builder
+     *
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(JsonArray array) {
+        return JsonProvider.provider().createArrayBuilder(array);
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with the content of specified {@code collection}.
+     * If the @{code collection} contains {@link Optional}s then resulting JSON array builder
+     * contains the value from the {@code collection} only if the {@link Optional} is not empty.
+     *
+     * @param collection the initial data for the builder
+     * @return a JSON array builder
+     * @exception IllegalArgumentException if the value from the {@code collection} cannot be converted
+     *            to the corresponding {@link JsonValue}
+     *
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(Collection<?> collection) {
+        return JsonProvider.provider().createArrayBuilder(collection);
+    }
+
+    /**
+     * Creates a JSON object builder
+     *
+     * @return a JSON object builder
+     */
+    public static JsonObjectBuilder createObjectBuilder() {
+        return JsonProvider.provider().createObjectBuilder();
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the specified object.
+     *
+     * @param object the initial object in the builder
+     * @return a JSON object builder
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(JsonObject object) {
+        return JsonProvider.provider().createObjectBuilder(object);
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the data from specified {@code map}.
+     * If the @{code map} contains {@link Optional}s then resulting JSON object builder
+     * contains the key from the {@code map} only if the {@link Optional} is not empty.
+     *
+     * @param map the initial object in the builder
+     * @return a JSON object builder
+     * @exception IllegalArgumentException if the value from the {@code map} cannot be converted
+     *            to the corresponding {@link JsonValue}
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        return JsonProvider.provider().createObjectBuilder(map);
+    }
+
+    /**
+     * Creates JSON Pointer (<a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>)
+     * from given {@code jsonPointer} string.
+     * <ul>
+     *     <li>An empty {@code jsonPointer} string defines a reference to the target itself.</li>
+     *     <li>If the {@code jsonPointer} string is non-empty, it must be a sequence of '{@code /}' prefixed tokens.</li>
+     * </ul>
+     *
+     * @param jsonPointer the valid escaped JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer
+     * @return a JSON Pointer
+     *
+     * @since 1.1
+     */
+    public static JsonPointer createPointer(String jsonPointer) {
+        return JsonProvider.provider().createPointer(jsonPointer);
+    }
+
+    /**
+     * Creates a JSON Patch builder (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>).
+     *
+     * @return a JSON Patch builder
+     *
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder() {
+        return JsonProvider.provider().createPatchBuilder();
+    }
+
+    /**
+     * Creates a JSON Patch builder
+     * (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>),
+     * initialized with the specified operations.
+     *
+     * @param array the initial patch operations
+     * @return a JSON Patch builder
+     *
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder(JsonArray array) {
+        return JsonProvider.provider().createPatchBuilder(array);
+    }
+
+    /**
+     * Creates a JSON Patch (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>)
+     * from the specified operations.
+     *
+     * @param array patch operations
+     * @return a JSON Patch
+     *
+     * @since 1.1
+     */
+    public static JsonPatch createPatch(JsonArray array) {
+        return JsonProvider.provider().createPatch(array);
+    }
+
+    /**
+     * Generates a JSON Patch (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>)
+     * from the source and target {@code JsonStructure}.
+     * The generated JSON Patch need not be unique.
+     *
+     * @param source the source
+     * @param target the target, must be the same type as the source
+     * @return a JSON Patch which when applied to the source, yields the target
+     *
+     * @since 1.1
+     */
+    public static JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        return JsonProvider.provider().createDiff(source, target);
+    }
+
+    /**
+     * Creates JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from specified {@code JsonValue}.
+     *
+     * @param patch the patch
+     * @return a JSON Merge Patch
+     *
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergePatch(JsonValue patch) {
+        return JsonProvider.provider().createMergePatch(patch);
+    }
+
+    /**
+     * Generates a JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from the source and target {@code JsonValue}s
+     * which when applied to the {@code source}, yields the {@code target}.
+     *
+     * @param source the source
+     * @param target the target
+     * @return a JSON Merge Patch
+     *
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        return JsonProvider.provider().createMergeDiff(source, target);
+    }
+
+    /**
+     * Creates a builder factory for creating {@link JsonArrayBuilder}
+     * and {@link JsonObjectBuilder} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON builders. The map may be empty or null
+     * @return a JSON builder factory
+     */
+    public static JsonBuilderFactory createBuilderFactory(
+            Map<String, ?> config) {
+        return JsonProvider.provider().createBuilderFactory(config);
+    }
+
+    /**
+     * Creates a JsonString.
+     *
+     * @param value a JSON string
+     * @return the JsonString for the string
+     *
+     * @since 1.1
+     */
+    public static JsonString createValue(String value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public static JsonNumber createValue(int value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public static JsonNumber createValue(long value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public static JsonNumber createValue(double value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public static JsonNumber createValue(BigDecimal value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public static JsonNumber createValue(BigInteger value) {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Encodes (escapes) a passed string as defined by <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+     * This method doesn't validate the passed JSON-pointer string.
+     *
+     * @param pointer the JSON-pointer string to encode
+     * @return encoded JSON-pointer string
+     * 
+     * @since 1.1
+     */
+    public static String encodePointer(String pointer) {
+        return pointer.replace("~", "~0").replace("/", "~1");
+    }
+
+    /**
+     * Decodes a passed JSON-pointer string as defined by <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+     * This method doesn't validate the passed JSON-pointer string.
+     *
+     * @param escaped the JSON-pointer string to decode
+     * @return decoded JSON-pointer string
+     *     
+     * @since 1.1
+     */
+    public static String decodePointer(String escaped) {
+        return escaped.replace("~1", "/").replace("~0", "~");
+    }
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonArray.java b/api/src/main/java/jakarta/json/JsonArray.java
new file mode 100644
index 0000000..7c39674
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonArray.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * {@code JsonArray} represents an immutable JSON array
+ * (an ordered sequence of zero or more values).
+ * It also provides an unmodifiable list view of the values in the array.
+ *
+ * <p>A {@code JsonArray} object can be created by reading JSON data from
+ * an input source or it can be built from scratch using an array builder
+ * object.
+ *
+ * <p>The following example demonstrates how to create a {@code JsonArray}
+ * object from an input source using the method {@link JsonReader#readArray()}:
+ * <pre><code>
+ * JsonReader jsonReader = Json.createReader(...);
+ * JsonArray array = jsonReader.readArray();
+ * jsonReader.close();
+ * </code></pre>
+ *
+ * <p>The following example demonstrates how to build an empty JSON array
+ * using the class {@link JsonArrayBuilder}:
+ * <pre><code>
+ * JsonArray array = Json.createArrayBuilder().build();
+ * </code></pre>
+ *
+ * <p>The example code below demonstrates how to create the following JSON array:
+ * <pre><code>
+ * [
+ *     { "type": "home", "number": "212 555-1234" },
+ *     { "type": "fax", "number": "646 555-4567" }
+ * ]
+ * </code></pre>
+ * <pre><code>
+ * JsonArray value = Json.createArrayBuilder()
+ *     .add(Json.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(Json.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * </code></pre>
+ *
+ * <p>The following example demonstrates how to write a {@code JsonArray} object 
+ * as JSON data:
+ * <pre><code>
+ * JsonArray arr = ...;
+ * JsonWriter writer = Json.createWriter(...)
+ * writer.writeArray(arr);
+ * writer.close();
+ * </code></pre>
+ *
+ * <p>The values in a {@code JsonArray} can be of the following types:
+ * {@link JsonObject}, {@link JsonArray},
+ * {@link JsonString}, {@link JsonNumber}, {@link JsonValue#TRUE},
+ * {@link JsonValue#FALSE}, and {@link JsonValue#NULL}. 
+ * {@code JsonArray} provides various accessor methods to access the values
+ * in an array.
+ * 
+ * <p>The following example shows how to obtain the home phone number 
+ * "212 555-1234" from the array built in the previous example:
+ * <pre><code>
+ * JsonObject home = array.getJsonObject(0);
+ * String number = home.getString("number");
+ * </code></pre>
+ *
+ * <p>{@code JsonArray} instances are list objects that provide read-only 
+ * access to the values in the JSON array. Any attempt to modify the list,
+ * whether directly or using its collection views, results in an 
+ * {@code UnsupportedOperationException}.
+ */
+public interface JsonArray extends JsonStructure, List<JsonValue> {
+
+    /**
+     * Returns the object value at the specified position in this array.
+     * This is a convenience method for {@code (JsonObject)get(index)}.
+     *
+     * @param index index of the value to be returned
+     * @return the value at the specified position in this array
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to the JsonObject type
+     */
+    JsonObject getJsonObject(int index);
+
+    /**
+     * Returns the array value at the specified position in this array.
+     * This is a convenience method for {@code (JsonArray)get(index)}.
+     *
+     * @param index index of the value to be returned
+     * @return the value at the specified position in this array
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to the JsonArray type
+     */
+    JsonArray getJsonArray(int index);
+
+    /**
+     * Returns the number value at the specified position in this array.
+     * This is a convenience method for {@code (JsonNumber)get(index)}.
+     *
+     * @param index index of the value to be returned
+     * @return the value at the specified position in this array
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to the JsonNumber type
+     */
+    JsonNumber getJsonNumber(int index);
+
+    /**
+     * Returns the string value at ths specified position in this array.
+     * This is a convenience method for {@code (JsonString)get(index)}.
+     *
+     * @param index index of the value to be returned
+     * @return the value at the specified position in this array
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to the JsonString type
+     */
+    JsonString getJsonString(int index);
+
+    /**
+     * Returns a list view of the specified type for the array. This method
+     * does not verify if there is a value of wrong type in the array. Providing
+     * this typesafe view dynamically may cause a program fail with a
+     * {@code ClassCastException}, if there is a value of wrong type in this
+     * array. Unfortunately, the exception can occur at any time after this
+     * method returns.
+     *
+     * @param <T> The type of the List for the array
+     * @param clazz a JsonValue type
+     * @return a list view of the specified type
+     */
+    <T extends JsonValue> List<T> getValuesAs(Class<T> clazz);
+
+    /**
+     * Returns a list view for the array. The value and the type of the elements
+     * in the list is specified by the {@code func} argument.
+     * <p>This method can be used to obtain a list of the unwrapped types, such as
+     * <pre>{@code
+     *     List<String> strings = ary1.getValuesAs(JsonString::getString);
+     *     List<Integer> ints = ary2.getValuesAs(JsonNumber::intValue);
+     * } </pre>
+     * or a list of simple projections, such as
+     * <pre> {@code
+     *     List<Integer> stringsizes = ary1.getValueAs((JsonString v)->v.getString().length();
+     * } </pre>
+     * @param <K> The element type (must be a subtype of JsonValue) of this JsonArray.
+     * @param <T> The element type of the returned List
+     * @param func The function that maps the elements of this JsonArray to the target elements.
+     * @return A List of the specified values and type.
+     * @throws ClassCastException if the {@code JsonArray} contains a value of wrong type
+     *
+     * @since 1.1
+     */
+    default <T, K extends JsonValue> List<T> getValuesAs(Function<K, T> func) {
+        @SuppressWarnings("unchecked")
+        Stream<K> stream = (Stream<K>) stream();
+        return stream.map(func).collect(Collectors.toList());
+    }
+
+    /**
+     * A convenience method for
+     * {@code getJsonString(index).getString()}.
+     *
+     * @param index index of the {@code JsonString} value
+     * @return the String value at the specified position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to {@code JsonString}
+     */
+    String getString(int index);
+
+    /**
+     * Returns the {@code String} value of {@code JsonString} at the specified
+     * position in this JSON array values. If {@code JsonString} is found,
+     * its {@link jakarta.json.JsonString#getString()} is returned. Otherwise,
+     * the specified default value is returned.
+     *
+     * @param index index of the {@code JsonString} value
+     * @param defaultValue the String to return if the {@code JsonValue} at the
+     *    specified position is not a {@code JsonString}
+     * @return the String value at the specified position in this array,
+     * or the specified default value
+     */
+    String getString(int index, String defaultValue);
+
+    /**
+     * A convenience method for
+     * {@code getJsonNumber(index).intValue()}.
+     *
+     * @param index index of the {@code JsonNumber} value
+     * @return the int value at the specified position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to {@code JsonNumber}
+     */
+    int getInt(int index);
+
+    /**
+     * Returns the int value of the {@code JsonNumber} at the specified position. 
+     * If the value at that position is a {@code JsonNumber},
+     * this method returns {@link jakarta.json.JsonNumber#intValue()}. Otherwise
+     * this method returns the specified default value.
+     *
+     * @param index index of the {@code JsonNumber} value
+     * @param defaultValue the int value to return if the {@code JsonValue} at
+     *     the specified position is not a {@code JsonNumber}
+     * @return the int value at the specified position in this array,
+     * or the specified default value
+     */
+    int getInt(int index, int defaultValue);
+
+    /**
+     * Returns the boolean value at the specified position.
+     * If the value at the specified position is {@code JsonValue.TRUE} 
+     * this method returns {@code true}. If the value at the specified position 
+     * is {@code JsonValue.FALSE} this method returns {@code false}.
+     *
+     * @param index index of the JSON boolean value
+     * @return the boolean value at the specified position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     * assignable to {@code JsonValue.TRUE} or {@code JsonValue.FALSE}
+     */
+    boolean getBoolean(int index);
+
+    /**
+     * Returns the boolean value at the specified position.
+     * If the value at the specified position is {@code JsonValue.TRUE}
+     * this method returns {@code true}. If the value at the specified position 
+     * is {@code JsonValue.FALSE} this method returns {@code false}. 
+     * Otherwise this method returns the specified default value.
+     *
+     * @param index index of the JSON boolean value
+     * @param defaultValue the boolean value to return if the {@code JsonValue}
+     *    at the specified position is neither TRUE nor FALSE
+     * @return the boolean value at the specified position,
+     * or the specified default value
+     */
+    boolean getBoolean(int index, boolean defaultValue);
+
+    /**
+     * Returns {@code true} if the value at the specified location in this
+     * array is {@code JsonValue.NULL}.
+     *
+     * @param index index of the JSON null value
+     * @return return true if the value at the specified location is
+     * {@code JsonValue.NULL}, otherwise false
+     * @throws IndexOutOfBoundsException if the index is out of range
+     */
+    boolean isNull(int index);
+}
diff --git a/api/src/main/java/jakarta/json/JsonArrayBuilder.java b/api/src/main/java/jakarta/json/JsonArrayBuilder.java
new file mode 100644
index 0000000..db0c935
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonArrayBuilder.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A builder for creating {@link JsonArray} models from scratch, and for
+ * modifying a existing {@code JsonArray}.
+ * <p>A {@code JsonArrayBuilder} can start with an empty or a non-empty
+ * JSON array model. This interface provides methods to add, insert, remove
+ * and replace values in the JSON array model.</p>
+ * <p>Methods in this class can be chained to perform multiple values to
+ * the array.</p>
+ *
+ * <p>The class {@link jakarta.json.Json} contains methods to create the builder
+ * object. The example code below shows how to build an empty {@code JsonArray}
+ * instance.
+ * <pre>
+ * <code>
+ * JsonArray array = Json.createArrayBuilder().build();
+ * </code>
+ * </pre>
+ *
+ * <p>The class {@link JsonBuilderFactory} also contains methods to create
+ * {@code JsonArrayBuilder} instances. A factory instance can be used to create
+ * multiple builder instances with the same configuration. This the preferred
+ * way to create multiple instances.
+ *
+ * The example code below shows how to build a {@code JsonArray} object
+ * that represents the following JSON array:
+ *
+ * <pre>
+ * <code>
+ * [
+ *     { "type": "home", "number": "212 555-1234" },
+ *     { "type": "fax", "number": "646 555-4567" }
+ * ]
+ * </code>
+ * </pre>
+ *
+ * <p>The following code creates the JSON array above:
+ *
+ * <pre>
+ * <code>
+ * JsonBuilderFactory factory = Json.createBuilderFactory(config);
+ * JsonArray value = factory.createArrayBuilder()
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * </code>
+ * </pre>
+ *
+ * <p>This class does <em>not</em> allow <code>null</code> to be used as a
+ * value while building the JSON array
+ *
+ * @see JsonObjectBuilder
+ */
+public interface JsonArrayBuilder {
+
+    /**
+     * Adds a value to the array.
+     *
+     * @param value the JSON value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     */
+    JsonArrayBuilder add(JsonValue value);
+
+    /**
+     * Adds a value to the array as a {@link JsonString}.
+     *
+     * @param value the string value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     */
+    JsonArrayBuilder add(String value);
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber}.
+     *
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     *
+     * @see JsonNumber
+     */
+    JsonArrayBuilder add(BigDecimal value);
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber}.
+     *
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     *
+     * @see JsonNumber
+     */
+    JsonArrayBuilder add(BigInteger value);
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber}.
+     *
+     * @param value the number value
+     * @return this array builder
+     *
+     * @see JsonNumber
+     */
+    JsonArrayBuilder add(int value);
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber}.
+     *
+     * @param value the number value
+     * @return this array builder
+     *
+     * @see JsonNumber
+     */
+    JsonArrayBuilder add(long value);
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber}.
+     *
+     * @param value the number value
+     * @return this array builder
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or
+     *      infinity
+     *
+     * @see JsonNumber
+     */
+    JsonArrayBuilder add(double value);
+
+    /**
+     * Adds a {@link JsonValue#TRUE}  or {@link JsonValue#FALSE} value to the
+     * array.
+     *
+     * @param value the boolean value
+     * @return this array builder
+     */
+    JsonArrayBuilder add(boolean value);
+
+    /**
+     * Adds a {@link JsonValue#NULL} value to the array.
+     *
+     * @return this array builder
+     */
+    JsonArrayBuilder addNull();
+
+    /**
+     * Adds a {@link JsonObject} from an object builder to the array.
+     *
+     * @param builder the object builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     */
+    JsonArrayBuilder add(JsonObjectBuilder builder);
+
+    /**
+     * Adds a {@link JsonArray} from an array builder to the array.
+     *
+     * @param builder the array builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     */
+    JsonArrayBuilder add(JsonArrayBuilder builder);
+
+    /**
+     * Adds all elements of the array in the specified array builder to the array.
+     *
+     * @param builder the array builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     *
+     @since 1.1
+     */
+    default JsonArrayBuilder addAll(JsonArrayBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Inserts a value to the array at the specified position. Shifts the value
+     * currently at that position (if any) and any subsequent values to the right
+     * (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the JSON value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, JsonValue value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonString} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the string value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, BigDecimal value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, BigInteger value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, long value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a value to the array as a {@link JsonNumber} at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or
+     *      infinity
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a {@link JsonValue#TRUE}  or {@link JsonValue#FALSE} value to the
+     * array at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param value the boolean value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, boolean value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a {@link JsonValue#NULL} value to the array at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder addNull(int index) {
+        return add(index, JsonValue.NULL);
+    }
+
+    /**
+     * Adds a {@link JsonObject} from an object builder to the array at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param builder the object builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, JsonObjectBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a {@link JsonArray} from an array builder to the array at the specified position.
+     * Shifts the value currently at that position (if any) and any subsequent values
+     * to the right (adds one to their indices).  Index starts with 0.
+     *
+     * @param index the position in the array
+     * @param builder the array builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index > array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder add(int index, JsonArrayBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value at the
+     * specified position.
+     *
+     * @param index the position in the array
+     * @param value the JSON value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, JsonValue value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonString} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the string value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonNumber} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, BigDecimal value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonNumber} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NullPointerException if the specified value is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, BigInteger value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonNumber} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonNumber} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, long value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonNumber} at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the number value
+     * @return this array builder
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or
+     *      infinity
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @see JsonNumber
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with
+     * a {@link JsonValue#TRUE}  or {@link JsonValue#FALSE} value
+     * at the specified position.
+     *
+     * @param index the position in the array
+     * @param value the boolean value
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, boolean value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with
+     * a {@link JsonValue#NULL} value at the specified position.
+     *
+     * @param index the position in the array
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder setNull(int index) {
+        return set(index, JsonValue.NULL);
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonObject} from an object builder at the specified position.
+     *
+     * @param index the position in the array
+     * @param builder the object builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, JsonObjectBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Replaces a value in the array with the specified value as a
+     * {@link JsonArray} from an array builder at the specified position.
+     *
+     * @param index the position in the array
+     * @param builder the array builder
+     * @return this array builder
+     * @throws NullPointerException if the specified builder is null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder set(int index, JsonArrayBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Remove the value in the array at the specified position.
+     * Shift any subsequent values to the left (subtracts one from their
+     * indices.
+     *
+     * @param index the position in the array
+     * @return this array builder
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *   {@code (index < 0 || index >= array size)}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder remove(int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the current array.
+     *
+     * @return the current JSON array
+     */
+    JsonArray build();
+
+}
+
diff --git a/api/src/main/java/jakarta/json/JsonBuilderFactory.java b/api/src/main/java/jakarta/json/JsonBuilderFactory.java
new file mode 100644
index 0000000..eacfd1e
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonBuilderFactory.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Factory to create {@link JsonObjectBuilder} and {@link JsonArrayBuilder}
+ * instances. If a factory instance is configured with some configuration,
+ * that would be used to configure the created builder instances.
+ *
+ * <p>
+ * {@code JsonObjectBuilder} and {@code JsonArrayBuilder} can also be created
+ * using {@link Json}'s methods. If multiple builder instances are created,
+ * then creating them using a builder factory is preferred.
+ *
+ * <p>
+ * <b>For example:</b>
+ * <pre>
+ * <code>
+ * JsonBuilderFactory factory = Json.createBuilderFactory(...);
+ * JsonArray value = factory.createArrayBuilder()
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * </code>
+ * </pre>
+ *
+ * <p> All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public interface JsonBuilderFactory {
+
+    /**
+     * Creates a {@code JsonObjectBuilder} instance that is used to build
+     * {@link JsonObject}.
+     *
+     * @return a JSON object builder
+     */
+    JsonObjectBuilder createObjectBuilder();
+
+    /**
+     * Creates a {@code JsonObjectBuilder} instance, initialized with an object.
+     *
+     * @param object the initial object in the builder
+     * @return a JSON object builder
+     * @throws NullPointerException if specified object is {@code null}
+     *
+     * @since 1.1
+     */
+    default JsonObjectBuilder createObjectBuilder(JsonObject object) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a {@code JsonObjectBuilder} instance, initialized with the specified object.
+     *
+     * @param object the initial object in the builder
+     * @return a JSON object builder
+     * @throws NullPointerException if specified object is {@code null}
+     *
+     * @since 1.1
+     */
+    default JsonObjectBuilder createObjectBuilder(Map<String, Object> object) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a {@code JsonArrayBuilder} instance that is used to build
+     * {@link JsonArray}
+     *
+     * @return a JSON array builder
+     */
+    JsonArrayBuilder createArrayBuilder();
+
+    /**
+     * Creates a {@code JsonArrayBuilder} instance, initialized with an array.
+     *
+     * @param array the initial array in the builder
+     * @return a JSON array builder
+     * @throws NullPointerException if specified array is {@code null}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder createArrayBuilder(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a {@code JsonArrayBuilder} instance,
+     * initialized with the content of specified collection.
+     *
+     * @param collection the initial data for the builder
+     * @return a JSON array builder
+     * @throws NullPointerException if specified collection is {@code null}
+     *
+     * @since 1.1
+     */
+    default JsonArrayBuilder createArrayBuilder(Collection<?> collection) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns read-only map of supported provider specific configuration
+     * properties that are used to configure the created JSON builders.
+     * If there are any specified configuration properties that are not
+     * supported by the provider, they won't be part of the returned map.
+     *
+     * @return a map of supported provider specific properties that are used
+     * to configure the builders. The map be empty but not null.
+     */
+    Map<String, ?> getConfigInUse();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonException.java b/api/src/main/java/jakarta/json/JsonException.java
new file mode 100644
index 0000000..403cb75
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * <code>JsonException</code> indicates that some exception happened during
+ * JSON processing.
+ */
+public class JsonException extends RuntimeException {
+
+    /**
+     * Constructs a new runtime exception with the specified detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *          later retrieval by the {@link #getMessage()} method.
+     */
+    public JsonException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this runtime exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *         by the {@link #getMessage()} method).
+     * @param cause the cause (which is saved for later retrieval by the
+     *         {@link #getCause()} method). (A <code>null</code> value is
+     *         permitted, and indicates that the cause is nonexistent or
+     *         unknown.)
+     */
+    public JsonException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
+
diff --git a/api/src/main/java/jakarta/json/JsonMergePatch.java b/api/src/main/java/jakarta/json/JsonMergePatch.java
new file mode 100644
index 0000000..07b64d8
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonMergePatch.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * <p>This interface represents an implementation of a JSON Merge Patch
+ * as defined by <a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>.
+ * </p>
+ * <p>A {@code JsonMergePatch} can be instantiated with {@link Json#createMergePatch(JsonValue)}
+ * by specifying the patch operations in a JSON Merge Patch or using {@link Json#createMergeDiff(JsonValue, JsonValue)}
+ * to create a JSON Merge Patch based on the difference between two {@code JsonValue}s.
+ * </p>
+ * The following illustrates both approaches.
+ * <p>1. Construct a JsonMergePatch with an existing JSON Merge Patch.
+ * <pre>{@code
+ *   JsonValue contacts = ... ; // The target to be patched
+ *   JsonValue patch = ...  ; // JSON Merge Patch
+ *   JsonMergePatch mergePatch = Json.createMergePatch(patch);
+ *   JsonValue result = mergePatch.apply(contacts);
+ * } </pre>
+ * 2. Construct a JsonMergePatch from a difference between two {@code JsonValue}s.
+ * <pre>{@code
+ *   JsonValue source = ... ; // The source object
+ *   JsonValue target = ... ; // The modified object
+ *   JsonMergePatch mergePatch = Json.createMergeDiff(source, target); // The diff between source and target in a Json Merge Patch format
+ * } </pre>
+ *
+ * @see <a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>
+ *
+ * @since 1.1
+ */
+public interface JsonMergePatch {
+
+    /**
+     * Applies the JSON Merge Patch to the specified {@code target}.
+     * The target is not modified by the patch.
+     *
+     * @param target the target to apply the merge patch
+     * @return the transformed target after the patch
+     */
+    JsonValue apply(JsonValue target);
+
+    /**
+     * Returns the {@code JsonMergePatch} as {@code JsonValue}.
+     *
+     * @return this {@code JsonMergePatch} as {@code JsonValue}
+     */
+    JsonValue toJsonValue();
+}
diff --git a/api/src/main/java/jakarta/json/JsonNumber.java b/api/src/main/java/jakarta/json/JsonNumber.java
new file mode 100644
index 0000000..6064cb1
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonNumber.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * An immutable JSON number value.
+ *
+ * <p>
+ * Implementations may use a {@link BigDecimal} object to store the numeric
+ * value internally.
+ * The {@code BigDecimal} object can be constructed from the following types:
+ * <code>int</code> {@link BigDecimal#BigDecimal(int)},
+ * <code>long</code> {@link BigDecimal#BigDecimal(long)},
+ * <code>BigInteger</code> {@link BigDecimal#BigDecimal(BigInteger)},
+ * <code>double</code> {@link BigDecimal#valueOf(double)}, and
+ * <code>String</code> {@link BigDecimal#BigDecimal(String)}.
+ * Some of the method semantics in this class are defined using the
+ * {@code BigDecimal} semantics.
+ */
+public interface JsonNumber extends JsonValue {
+
+    /**
+     * Returns true if this JSON number is a integral number. This method
+     * semantics are defined using {@code bigDecimalValue().scale()}. If the
+     * scale is zero, then it is considered integral type. This integral type
+     * information can be used to invoke an appropriate accessor method to
+     * obtain a numeric value as in the following example:
+     *
+     * <pre>
+     * <code>
+     * JsonNumber num = ...
+     * if (num.isIntegral()) {
+     *     num.longValue();     // or other methods to get integral value
+     * } else {
+     *     num.doubleValue();   // or other methods to get decimal number value
+     * }
+     * </code>
+     * </pre>
+     *
+     * @return true if this number is a integral number, otherwise false
+     */
+    boolean isIntegral();
+
+    /**
+     * Returns this JSON number as an {@code int}. Note that this conversion
+     * can lose information about the overall magnitude and precision of the
+     * number value as well as return a result with the opposite sign.
+     *
+     * @return an {@code int} representation of the JSON number
+     * @see java.math.BigDecimal#intValue()
+     */
+    int intValue();
+
+    /**
+     * Returns this JSON number as an {@code int}.
+     *
+     * @return an {@code int} representation of the JSON number
+     * @throws ArithmeticException if the number has a nonzero fractional
+     *         part or if it does not fit in an {@code int}
+     * @see java.math.BigDecimal#intValueExact()
+     */
+    int intValueExact();
+
+    /**
+     * Returns this JSON number as a {@code long}. Note that this conversion
+     * can lose information about the overall magnitude and precision of the
+     * number value as well as return a result with the opposite sign.
+     *
+     * @return a {@code long} representation of the JSON number.
+     * @see java.math.BigDecimal#longValue()
+     */
+    long longValue();
+
+    /**
+     * Returns this JSON number as a {@code long}.
+     *
+     * @return a {@code long} representation of the JSON number
+     * @throws ArithmeticException if the number has a non-zero fractional
+     *         part or if it does not fit in a {@code long}
+     * @see java.math.BigDecimal#longValueExact()
+     */
+    long longValueExact();
+
+    /**
+     * Returns this JSON number as a {@link BigInteger} object. This is a
+     * a convenience method for {@code bigDecimalValue().toBigInteger()}.
+     * Note that this conversion can lose information about the overall
+     * magnitude and precision of the number value as well as return a result
+     * with the opposite sign.
+     *
+     * @return a {@code BigInteger} representation of the JSON number.
+     * @see java.math.BigDecimal#toBigInteger()
+     */
+    BigInteger bigIntegerValue();
+
+    /**
+     * Returns this JSON number as a {@link BigInteger} object. This is a
+     * convenience method for {@code bigDecimalValue().toBigIntegerExact()}.
+     *
+     * @return a {@link BigInteger} representation of the JSON number
+     * @throws ArithmeticException if the number has a nonzero fractional part
+     * @see java.math.BigDecimal#toBigIntegerExact()
+     */
+    BigInteger bigIntegerValueExact();
+
+    /**
+     * Returns this JSON number as a {@code double}. This is a
+     * a convenience method for {@code bigDecimalValue().doubleValue()}.
+     * Note that this conversion can lose information about the overall
+     * magnitude and precision of the number value as well as return a result
+     * with the opposite sign.
+     *
+     * @return a {@code double} representation of the JSON number
+     * @see java.math.BigDecimal#doubleValue()
+     */
+    double doubleValue();
+
+    /**
+     * Returns this JSON number as a {@link BigDecimal} object.
+     *
+     * @return a {@link BigDecimal} representation of the JSON number
+     */
+    BigDecimal bigDecimalValue();
+
+    /**
+     * Returns this JSON number as a {@link Number} object.
+     *
+     * @return a {@link Number} representation of the JSON number
+     *
+     * @since 1.1
+     */
+    default Number numberValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a JSON text representation of the JSON number. The
+     * representation is equivalent to {@link BigDecimal#toString()}.
+     *
+     * @return JSON text representation of the number
+     */
+    @Override
+    String toString();
+
+    /**
+     * Compares the specified object with this {@code JsonNumber} object for
+     * equality. Returns {@code true} if and only if the type of the specified
+     * object is also {@code JsonNumber} and their {@link #bigDecimalValue()}
+     * objects are <i>equal</i>
+     *
+     * @param obj the object to be compared for equality with
+     *      this {@code JsonNumber}
+     * @return {@code true} if the specified object is equal to this
+     *      {@code JsonNumber}
+     */
+    @Override
+    boolean equals(Object obj);
+
+    /**
+     * Returns the hash code value for this {@code JsonNumber} object.  The
+     * hash code of a {@code JsonNumber} object is defined as the hash code of
+     * its {@link #bigDecimalValue()} object.
+     *
+     * @return the hash code value for this {@code JsonNumber} object
+     */
+    @Override
+    int hashCode();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonObject.java b/api/src/main/java/jakarta/json/JsonObject.java
new file mode 100644
index 0000000..61f1998
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonObject.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.util.Map;
+
+/**
+ * {@code JsonObject} class represents an immutable JSON object value
+ * (an unordered collection of zero or more name/value pairs).
+ * It also provides unmodifiable map view to the JSON object
+ * name/value mappings.
+ *
+ * <p>A JsonObject instance can be created from an input source using
+ * {@link JsonReader#readObject()}. For example:
+ * <pre><code>
+ * JsonReader jsonReader = Json.createReader(...);
+ * JsonObject object = jsonReader.readObject();
+ * jsonReader.close();
+ * </code></pre>
+ *
+ * It can also be built from scratch using a {@link JsonObjectBuilder}.
+ *
+ * <p>For example 1: An empty JSON object can be built as follows:
+ * <pre><code>
+ * JsonObject object = Json.createObjectBuilder().build();
+ * </code></pre>
+ *
+ * For example 2: The following JSON
+ * <pre><code>
+ * {
+ *     "firstName": "John", "lastName": "Smith", "age": 25,
+ *     "address" : {
+ *         "streetAddress": "21 2nd Street",
+ *         "city": "New York",
+ *         "state": "NY",
+ *         "postalCode": "10021"
+ *     },
+ *     "phoneNumber": [
+ *         { "type": "home", "number": "212 555-1234" },
+ *         { "type": "fax", "number": "646 555-4567" }
+ *     ]
+ * }
+ * </code></pre>
+ * can be built using :
+ * <pre><code>
+ * JsonObject value = Json.createObjectBuilder()
+ *     .add("firstName", "John")
+ *     .add("lastName", "Smith")
+ *     .add("age", 25)
+ *     .add("address", Json.createObjectBuilder()
+ *         .add("streetAddress", "21 2nd Street")
+ *         .add("city", "New York")
+ *         .add("state", "NY")
+ *         .add("postalCode", "10021"))
+ *     .add("phoneNumber", Json.createArrayBuilder()
+ *         .add(Json.createObjectBuilder()
+ *             .add("type", "home")
+ *             .add("number", "212 555-1234"))
+ *         .add(Json.createObjectBuilder()
+ *             .add("type", "fax")
+ *             .add("number", "646 555-4567")))
+ *     .build();
+ * </code></pre>
+ *
+ * {@code JsonObject} can be written to JSON as follows:
+ * <pre><code>
+ * JsonWriter writer = ...
+ * JsonObject obj = ...;
+ * writer.writeObject(obj);
+ * </code></pre>
+ *
+ * {@code JsonObject} values can be {@link JsonObject}, {@link JsonArray},
+ * {@link JsonString}, {@link JsonNumber}, {@link JsonValue#TRUE},
+ * {@link JsonValue#FALSE}, {@link JsonValue#NULL}. These values can be
+ * accessed using various accessor methods.
+ *
+ * <p>In the above example 2, "John" can be got using
+ * <pre><code>
+ * String firstName = object.getString("firstName");
+ * </code></pre>
+ *
+ * This map object provides read-only access to the JSON object data,
+ * and attempts to modify the map, whether direct or via its collection
+ * views, result in an {@code UnsupportedOperationException}.
+ *
+ * <p>The map object's iteration ordering is based on the order in which
+ * name/value pairs are added to the corresponding builder or the order
+ * in which name/value pairs appear in the corresponding stream.
+ */
+public interface JsonObject extends JsonStructure, Map<String, JsonValue> {
+
+    /**
+     * Returns the array value to which the specified name is mapped.
+     * This is a convenience method for {@code (JsonArray)get(name)} to
+     * get the value.
+     *
+     * @param name the name whose associated value is to be returned
+     * @return the array value to which the specified name is mapped, or
+     *         {@code null} if this object contains no mapping for the name
+     * @throws ClassCastException if the value to which the specified name
+     * is mapped is not assignable to JsonArray type
+     */
+    JsonArray getJsonArray(String name);
+
+    /**
+     * Returns the object value to which the specified name is mapped.
+     * This is a convenience method for {@code (JsonObject)get(name)} to
+     * get the value.
+     *
+     * @param name the name whose associated value is to be returned
+     * @return the object value to which the specified name is mapped, or
+     *         {@code null} if this object contains no mapping for the name
+     * @throws ClassCastException if the value to which the specified name
+     * is mapped is not assignable to JsonObject type
+     */
+    JsonObject getJsonObject(String name);
+
+    /**
+     * Returns the number value to which the specified name is mapped.
+     * This is a convenience method for {@code (JsonNumber)get(name)} to
+     * get the value.
+     *
+     * @param name the name whose associated value is to be returned
+     * @return the number value to which the specified name is mapped, or
+     *         {@code null} if this object contains no mapping for the name
+     * @throws ClassCastException if the value to which the specified name
+     * is mapped is not assignable to JsonNumber type
+     */
+    JsonNumber getJsonNumber(String name);
+
+    /**
+     * Returns the string value to which the specified name is mapped.
+     * This is a convenience method for {@code (JsonString)get(name)} to
+     * get the value.
+     *
+     * @param name the name whose associated value is to be returned
+     * @return the string value to which the specified name is mapped, or
+     *         {@code null} if this object contains no mapping for the name
+     * @throws ClassCastException if the value to which the specified name
+     * is mapped is not assignable to JsonString type
+     */
+    JsonString getJsonString(String name);
+
+    /**
+     * A convenience method for
+     * {@code getJsonString(name).getString()}
+     *
+     * @param name whose associated value is to be returned as String
+     * @return the String value to which the specified name is mapped
+     * @throws NullPointerException if the specified name doesn't have any
+     * mapping
+     * @throws ClassCastException if the value for specified name mapping
+     * is not assignable to JsonString
+     */
+    String getString(String name);
+
+    /**
+     * Returns the string value of the associated {@code JsonString} mapping
+     * for the specified name. If {@code JsonString} is found, then its
+     * {@link jakarta.json.JsonString#getString()} is returned. Otherwise,
+     * the specified default value is returned.
+     *
+     * @param name whose associated value is to be returned as String
+     * @param defaultValue a default value to be returned
+     * @return the string value of the associated mapping for the name,
+     * or the default value
+     */
+    String getString(String name, String defaultValue);
+
+    /**
+     * A convenience method for
+     * {@code getJsonNumber(name).intValue()}
+     *
+     * @param name whose associated value is to be returned as int
+     * @return the int value to which the specified name is mapped
+     * @throws NullPointerException if the specified name doesn't have any
+     * mapping
+     * @throws ClassCastException if the value for specified name mapping
+     * is not assignable to JsonNumber
+     */
+    int getInt(String name);
+
+    /**
+     * Returns the int value of the associated {@code JsonNumber} mapping
+     * for the specified name. If {@code JsonNumber} is found, then its
+     * {@link jakarta.json.JsonNumber#intValue()} is returned. Otherwise,
+     * the specified default value is returned.
+     *
+     * @param name whose associated value is to be returned as int
+     * @param defaultValue a default value to be returned
+     * @return the int value of the associated mapping for the name,
+     * or the default value
+     */
+    int getInt(String name, int defaultValue);
+
+    /**
+     * Returns the boolean value of the associated mapping for the specified
+     * name. If the associated mapping is JsonValue.TRUE, then returns true.
+     * If the associated mapping is JsonValue.FALSE, then returns false.
+     *
+     * @param name whose associated value is to be returned as boolean
+     * @return the boolean value to which the specified name is mapped
+     * @throws NullPointerException if the specified name doesn't have any
+     * mapping
+     * @throws ClassCastException if the value for specified name mapping
+     * is not assignable to JsonValue.TRUE or JsonValue.FALSE
+     */
+    boolean getBoolean(String name);
+
+    /**
+     * Returns the boolean value of the associated mapping for the specified
+     * name. If the associated mapping is JsonValue.TRUE, then returns true.
+     * If the associated mapping is JsonValue.FALSE, then returns false.
+     * Otherwise, the specified default value is returned.
+     *
+     * @param name whose associated value is to be returned as int
+     * @param defaultValue a default value to be returned
+     * @return the boolean value of the associated mapping for the name,
+     * or the default value
+     */
+    boolean getBoolean(String name, boolean defaultValue);
+
+    /**
+     * Returns {@code true} if the associated value for the specified name is
+     * {@code JsonValue.NULL}.
+     *
+     * @param name name whose associated value is checked
+     * @return return true if the associated value is {@code JsonValue.NULL},
+     * otherwise false
+     * @throws NullPointerException if the specified name doesn't have any
+     * mapping
+     */
+    boolean isNull(String name);
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonObjectBuilder.java b/api/src/main/java/jakarta/json/JsonObjectBuilder.java
new file mode 100644
index 0000000..b7fed79
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonObjectBuilder.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A builder for creating {@link JsonObject} models from scratch. This
+ * interface initializes an empty JSON object model and provides methods to add
+ * name/value pairs to the object model and to return the resulting object.
+ * The methods in this class can be chained to add multiple name/value pairs
+ * to the object.
+ *
+ * <p>The class {@link jakarta.json.Json} contains methods to create the builder
+ * object. The example code below shows how to build an empty {@code JsonObject}
+ * instance.
+ * <pre>
+ * <code>
+ * JsonObject object = Json.createObjectBuilder().build();
+ * </code>
+ * </pre>
+ *
+ * <p>The class {@link JsonBuilderFactory} also contains methods to create
+ * {@code JsonObjectBuilder} instances. A factory instance can be used to create
+ * multiple builder instances with the same configuration. This the preferred
+ * way to create multiple instances.
+ *
+ * The example code below shows how to build a {@code JsonObject} model that
+ * represents the following JSON object:
+ *
+ * <pre>
+ * <code>
+ * {
+ *     "firstName": "John", "lastName": "Smith", "age": 25,
+ *     "address" : {
+ *         "streetAddress": "21 2nd Street",
+ *         "city": "New York",
+ *         "state": "NY",
+ *         "postalCode": "10021"
+ *     },
+ *     "phoneNumber": [
+ *         { "type": "home", "number": "212 555-1234" },
+ *         { "type": "fax", "number": "646 555-4567" }
+ *     ]
+ * }
+ * </code>
+ * </pre>
+ *
+ * <p>The code to create the object shown above is the following:
+ *
+ * <pre>
+ * <code>
+ * JsonBuilderFactory factory = Json.createBuilderFactory(config);
+ * JsonObject value = factory.createObjectBuilder()
+ *     .add("firstName", "John")
+ *     .add("lastName", "Smith")
+ *     .add("age", 25)
+ *     .add("address", factory.createObjectBuilder()
+ *         .add("streetAddress", "21 2nd Street")
+ *         .add("city", "New York")
+ *         .add("state", "NY")
+ *         .add("postalCode", "10021"))
+ *     .add("phoneNumber", factory.createArrayBuilder()
+ *         .add(factory.createObjectBuilder()
+ *             .add("type", "home")
+ *             .add("number", "212 555-1234"))
+ *         .add(factory.createObjectBuilder()
+ *             .add("type", "fax")
+ *             .add("number", "646 555-4567")))
+ *     .build();
+ * </code>
+ * </pre>
+ *
+ * <p>This class does <em>not</em> allow <code>null</code> to be used as a name or
+ * value while building the JSON object
+ *
+ * @see JsonArrayBuilder
+ */
+public interface JsonObjectBuilder {
+
+    /**
+     * Adds a name/{@code JsonValue} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name or value is null
+     */
+    JsonObjectBuilder add(String name, JsonValue value);
+
+    /**
+     * Adds a name/{@code JsonString} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name or value is null
+     */
+    JsonObjectBuilder add(String name, String value);
+
+    /**
+     * Adds a name/{@code JsonNumber} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name or value is null
+     *
+     * @see JsonNumber
+     */
+    JsonObjectBuilder add(String name, BigInteger value);
+
+    /**
+     * Adds a name/{@code JsonNumber} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name or value is null
+     *
+     * @see JsonNumber
+     */
+    JsonObjectBuilder add(String name, BigDecimal value);
+
+    /**
+     * Adds a name/{@code JsonNumber} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name is null
+     *
+     * @see JsonNumber
+     */
+    JsonObjectBuilder add(String name, int value);
+
+    /**
+     * Adds a name/{@code JsonNumber} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name is null
+     *
+     * @see JsonNumber
+     */
+    JsonObjectBuilder add(String name, long value);
+
+    /**
+     * Adds a name/{@code JsonNumber} pair to the JSON object associated with
+     * this object builder. If the object contains a mapping for the specified
+     * name, this method replaces the old value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or 
+     * infinity
+     * @throws NullPointerException if the specified name is null
+     *
+     * @see JsonNumber
+     */
+    JsonObjectBuilder add(String name, double value);
+
+    /**
+     * Adds a name/{@code JsonValue#TRUE} or name/{@code JsonValue#FALSE} pair
+     * to the JSON object associated with this object builder. If the object
+     * contains a mapping for the specified name, this method replaces the old
+     * value with the specified value.
+     *
+     * @param name name in the name/value pair
+     * @param value value in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name is null
+     */
+    JsonObjectBuilder add(String name, boolean value);
+
+    /**
+     * Adds a name/{@code JsonValue#NULL} pair to the JSON object associated
+     * with this object builder where the value is {@code null}.
+     * If the object contains a mapping for the specified name, this method
+     * replaces the old value with {@code null}.
+     *
+     * @param name name in the name/value pair
+     * @return this object builder
+     * @throws NullPointerException if the specified name is null
+     */
+    JsonObjectBuilder addNull(String name);
+
+    /**
+     * Adds a name/{@code JsonObject} pair to the JSON object associated
+     * with this object builder. The value {@code JsonObject} is built from the
+     * specified object builder. If the object contains a mapping for the
+     * specified name, this method replaces the old value with the
+     * {@code JsonObject} from the specified object builder.
+     *
+     * @param name name in the name/value pair
+     * @param builder the value is the object associated with this builder
+     * @return this object builder
+     * @throws NullPointerException if the specified name or builder is null
+     */
+    JsonObjectBuilder add(String name, JsonObjectBuilder builder);
+
+    /**
+     * Adds a name/{@code JsonArray} pair to the JSON object associated with
+     * this object builder. The value {@code JsonArray} is built from the
+     * specified array builder. If the object contains a mapping for the
+     * specified name, this method replaces the old value with the
+     * {@code JsonArray} from the specified array builder.
+     *
+     * @param name the name in the name/value pair
+     * @param builder the value is the object array with this builder
+     * @return this object builder
+     * @throws NullPointerException if the specified name or builder is null
+     */
+    JsonObjectBuilder add(String name, JsonArrayBuilder builder);
+
+    /**
+     * Adds all name/value pairs in the JSON object associated with the specified
+     * object builder to the JSON object associated with this object builder.
+     * The newly added name/value pair will replace any existing name/value pair with
+     * the same name.
+     *
+     * @param builder the specified object builder
+     * @return this object builder
+     * @throws NullPointerException if the specified builder is null
+     * @since 1.1
+     */
+    default JsonObjectBuilder addAll(JsonObjectBuilder builder) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Remove the name/value pair from the JSON object associated with this
+     * object builder if it is present.
+     *
+     * @param name the name in the name/value pair to be removed
+     * @return this object builder
+     * @throws NullPointerException if the specified name is null
+     * @since 1.1
+     */
+    default JsonObjectBuilder remove(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the JSON object associated with this object builder. 
+     * The iteration order for the {@code JsonObject} is based
+     * on the order in which name/value pairs are added to the object using
+     * this builder.
+     *
+     * @return JSON object that is being built
+     */
+    JsonObject build();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonPatch.java b/api/src/main/java/jakarta/json/JsonPatch.java
new file mode 100644
index 0000000..dea4e36
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonPatch.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * <p>This interface represents an immutable implementation of a JSON Patch
+ * as defined by <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>.
+ * </p>
+ * <p>A {@code JsonPatch} can be instantiated with {@link Json#createPatch(JsonArray)}
+ * by specifying the patch operations in a JSON Patch. Alternately, it
+ * can also be constructed with a {@link JsonPatchBuilder}.
+ * </p>
+ * The following illustrates both approaches.
+ * <p>1. Construct a JsonPatch with a JSON Patch.
+ * <pre>{@code
+ *   JsonArray contacts = ... // The target to be patched
+ *   JsonArray patch = ...  ; // JSON Patch
+ *   JsonPatch jsonpatch = Json.createPatch(patch);
+ *   JsonArray result = jsonpatch.apply(contacts);
+ * } </pre>
+ * 2. Construct a JsonPatch with JsonPatchBuilder.
+ * <pre>{@code
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonArray result = builder.add("/John/phones/office", "1234-567")
+ *                             .remove("/Amy/age")
+ *                             .build()
+ *                             .apply(contacts);
+ * } </pre>
+ *
+ * @see <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>
+ *
+ * @since 1.1
+ */
+public interface JsonPatch {
+
+    /**
+     * This enum represents the list of valid JSON Patch operations
+     * as defined by <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>.
+     *
+     * @see <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>
+     */
+    enum Operation {
+
+        /**
+         * "add" operation.
+         */
+        ADD("add"),
+
+        /**
+         * "remove" operation.
+         */
+        REMOVE("remove"),
+
+        /**
+         * "replace" operation.
+         */
+        REPLACE("replace"),
+
+        /**
+         * "move" operation.
+         */
+        MOVE("move"),
+
+        /**
+         * "copy" operation.
+         */
+        COPY("copy"),
+
+        /**
+         * "test" operation.
+         */
+        TEST("test");
+
+        private final String operationName;
+
+        private Operation(String operationName) {
+            this.operationName = operationName;
+        }
+
+        /**
+         * Returns enum constant name as lower case string.
+         *
+         * @return lower case name of the enum constant
+         */
+        public String operationName() {
+            return operationName;
+        }
+
+        /**
+         * Returns the enum constant with the specified name.
+         *
+         * @param operationName {@code operationName} to convert to the enum constant.
+         * @return the enum constant for given {@code operationName}
+         * @throws JsonException if given {@code operationName} is not recognized
+         */
+        public static Operation fromOperationName(String operationName) {
+            for (Operation op : values()) {
+                if (op.operationName().equalsIgnoreCase(operationName)) {
+                    return op;
+                }
+            }
+            throw new JsonException("Illegal value for the operationName of the JSON patch operation: " + operationName);
+        }
+    }
+
+    /**
+     * Applies the patch operations to the specified {@code target}.
+     * The target is not modified by the patch.
+     *
+     * @param <T> the target type, must be a subtype of {@link JsonStructure}
+     * @param target the target to apply the patch operations
+     * @return the transformed target after the patch
+     * @throws JsonException if the supplied JSON Patch is malformed or if
+     *    it contains references to non-existing members
+     */
+    <T extends JsonStructure> T apply(T target);
+
+    /**
+     * Returns the {@code JsonPatch} as {@code JsonArray}.
+     *
+     * @return this {@code JsonPatch} as {@code JsonArray}
+     */
+    JsonArray toJsonArray();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonPatchBuilder.java b/api/src/main/java/jakarta/json/JsonPatchBuilder.java
new file mode 100644
index 0000000..0949839
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonPatchBuilder.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * A builder for constructing a JSON Patch as defined by
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a> by adding
+ * JSON Patch operations incrementally.
+ * <p>
+ * The following illustrates the approach.
+ * <pre>
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonPatch patch = builder.add("/John/phones/office", "1234-567")
+ *                            .remove("/Amy/age")
+ *                            .build();
+ * </pre>
+ * The result is equivalent to the following JSON Patch.
+ * <pre>
+ * [
+ *    {"op" = "add", "path" = "/John/phones/office", "value" = "1234-567"},
+ *    {"op" = "remove", "path" = "/Amy/age"}
+ * ] </pre>
+ *
+ * @see <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>
+ *
+ * @since 1.1
+ */
+public interface JsonPatchBuilder {
+
+    /**
+     * Adds an "add" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder add(String path, JsonValue value);
+
+    /**
+     * Adds an "add" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder add(String path, String value);
+
+    /**
+     * Adds an "add" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder add(String path, int value);
+
+    /**
+     * Adds an "add" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder add(String path, boolean value);
+
+    /**
+     * Adds a "remove" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder remove(String path);
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder replace(String path, JsonValue value);
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder replace(String path, String value);
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder replace(String path, int value);
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder replace(String path, boolean value);
+
+    /**
+     * Adds a "move" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param from the "from" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder move(String path, String from);
+
+    /**
+     * Adds a "copy" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param from the "from" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder copy(String path, String from);
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder test(String path, JsonValue value);
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder test(String path, String value);
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder test(String path, int value);
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     *
+     * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string.
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    JsonPatchBuilder test(String path, boolean value);
+
+
+    /**
+     * Returns the JSON Patch.
+     *
+     * @return a JSON Patch
+     */
+    JsonPatch build();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonPointer.java b/api/src/main/java/jakarta/json/JsonPointer.java
new file mode 100644
index 0000000..a42d8d3
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonPointer.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * <p>This interface represents an immutable implementation of a JSON Pointer
+ * as defined by <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+ * </p>
+ * <p> A JSON Pointer, when applied to a target {@link JsonValue},
+ * defines a reference location in the target.</p>
+ * <p> An empty JSON Pointer string defines a reference to the target itself.</p>
+ * <p> If the JSON Pointer string is non-empty, it must be a sequence
+ * of '/' prefixed tokens, and the target must either be a {@link JsonArray}
+ * or {@link JsonObject}. If the target is a {@code JsonArray}, the pointer
+ * defines a reference to an array element, and the last token specifies the index.
+ * If the target is a {@link JsonObject}, the pointer defines a reference to a
+ * name/value pair, and the last token specifies the name.
+ * </p>
+ * <p> The method {@link #getValue getValue()} returns the referenced value.
+ * Methods {@link #add add()}, {@link #replace replace()},
+ * and {@link #remove remove()} execute operations specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>. </p>
+ *
+ * @see <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>
+ * @see <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>
+ *
+ * @since 1.1
+ */
+public interface JsonPointer {
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     * <ol>
+     * <li>If the reference is the target (empty JSON Pointer string),
+     * the specified {@code value}, which must be the same type as
+     * specified {@code target}, is returned.</li>
+     * <li>If the reference is an array element, the specified {@code value} is inserted
+     * into the array, at the referenced index. The value currently at that location, and
+     * any subsequent values, are shifted to the right (adds one to the indices).
+     * Index starts with 0. If the reference is specified with a "-", or if the
+     * index is equal to the size of the array, the value is appended to the array.</li>
+     * <li>If the reference is a name/value pair of a {@code JsonObject}, and the
+     * referenced value exists, the value is replaced by the specified {@code value}.
+     * If the value does not exist, a new name/value pair is added to the object.</li>
+     * </ol>
+     *
+     * @param <T> the target type, must be a subtype of {@link JsonValue}
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the reference is an array element and
+     * the index is out of range ({@code index < 0 || index > array size}),
+     * or if the pointer contains references to non-existing objects or arrays.
+     */
+    <T extends JsonStructure> T add(T target, JsonValue value);
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}.
+     *
+     * @param <T> the target type, must be a subtype of {@link JsonValue}
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the referenced value does not exist,
+     *    or if the reference is the target.
+     */
+    <T extends JsonStructure> T remove(T target);
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param <T> the target type, must be a subtype of {@link JsonValue}
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the referenced value does not exist,
+     *    or if the reference is the target.
+     */
+    <T extends JsonStructure> T replace(T target, JsonValue value);
+
+    /**
+     * Returns {@code true} if there is a value at the referenced location in the specified {@code target}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return {@code true} if this pointer points to a value in a specified {@code target}.
+     */
+    boolean containsValue(JsonStructure target);
+
+    /**
+     * Returns the value at the referenced location in the specified {@code target}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the referenced value in the target.
+     * @throws NullPointerException if {@code target} is null
+     * @throws JsonException if the referenced value does not exist
+     */
+    JsonValue getValue(JsonStructure target);
+
+    /**
+     * Returns the string representation of this JSON Pointer.
+     * The value to be returned is an empty string or a sequence of '{@code /}' prefixed tokens.
+     *
+     * @return the valid escaped JSON Pointer string.
+     */
+    @Override
+    String toString();
+}
diff --git a/api/src/main/java/jakarta/json/JsonReader.java b/api/src/main/java/jakarta/json/JsonReader.java
new file mode 100644
index 0000000..b1192e6
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonReader.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.Closeable;
+
+/**
+ * Reads a JSON {@link JsonObject object} or an {@link JsonArray array}
+ * structure from an input source.
+ *
+ * <p>The class {@link jakarta.json.Json} contains methods to create readers from
+ * input sources ({@link java.io.InputStream} and {@link java.io.Reader}).
+ *
+ * <p>
+ * The following example demonstrates how to read an empty JSON array from
+ * a string:
+ * <pre>
+ * <code>
+ * JsonReader jsonReader = Json.createReader(new StringReader("[]"));
+ * JsonArray array = jsonReader.readArray();
+ * jsonReader.close();
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * The class {@link JsonReaderFactory} also contains methods to create
+ * {@code JsonReader} instances. A factory instance can be used to create
+ * multiple reader instances with the same configuration. This the preferred
+ * way to create multiple instances. A sample usage is shown in the following
+ * example:
+ * <pre>
+ * <code>
+ * JsonReaderFactory factory = Json.createReaderFactory(config);
+ * JsonReader reader1 = factory.createReader(...);
+ * JsonReader reader2 = factory.createReader(...);
+ * </code>
+ * </pre>
+ */
+public interface JsonReader extends  /*Auto*/Closeable {
+
+    /**
+     * Returns a JSON array or object that is represented in
+     * the input source. This method needs to be called
+     * only once for a reader instance.
+     *
+     * @return a JSON object or array
+     * @throws JsonException if a JSON object or array cannot
+     *     be created due to i/o error (IOException would be
+     * cause of JsonException)
+     * @throws jakarta.json.stream.JsonParsingException if a JSON object or array
+     *     cannot be created due to incorrect representation
+     * @throws IllegalStateException if read, readObject, readArray,
+     *     readValue or close method is already called
+     */
+    JsonStructure read();
+
+    /**
+     * Returns a JSON object that is represented in
+     * the input source. This method needs to be called
+     * only once for a reader instance.
+     *
+     * @return a JSON object
+     * @throws JsonException if a JSON object cannot
+     *     be created due to i/o error (IOException would be
+     *     cause of JsonException)
+     * @throws jakarta.json.stream.JsonParsingException if a JSON object cannot
+     *     be created due to incorrect representation
+     * @throws IllegalStateException if read, readObject, readArray,
+     *     readValue or close method is already called
+     */
+    JsonObject readObject();
+
+    /**
+     * Returns a JSON array that is represented in
+     * the input source. This method needs to be called
+     * only once for a reader instance.
+     *
+     * @return a JSON array
+     * @throws JsonException if a JSON array cannot
+     *     be created due to i/o error (IOException would be
+     *     cause of JsonException)
+     * @throws jakarta.json.stream.JsonParsingException if a JSON array cannot
+     *     be created due to incorrect representation
+     * @throws IllegalStateException if read, readObject, readArray,
+     *     readValue or close method is already called
+     */
+    JsonArray readArray();
+
+    /**
+     * Returns a JSON value that is represented in
+     * the input source. This method needs to be called
+     * only once for a reader instance.
+     *
+     * @return a JSON value
+     * @throws JsonException if a JSON value
+     *     be created due to i/o error (IOException would be
+     *     cause of JsonException)
+     * @throws jakarta.json.stream.JsonParsingException if a JSON value
+     *     cannot be created due to incorrect representation
+     * @throws IllegalStateException if read, readObject, readArray,
+     *     readValue or close method is already called
+     *
+     * @since 1.1
+     */
+    default JsonValue readValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Closes this reader and frees any resources associated with the
+     * reader. This method closes the underlying input source.
+     *
+     * @throws JsonException if an i/o error occurs (IOException would be
+     * cause of JsonException)
+     */
+    @Override
+    void close();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonReaderFactory.java b/api/src/main/java/jakarta/json/JsonReaderFactory.java
new file mode 100644
index 0000000..f0ff269
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonReaderFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * Factory to create {@link jakarta.json.JsonReader} instances. If a factory
+ * instance is configured with some configuration, that would be
+ * used to configure the created reader instances.
+ *
+ * <p>
+ * {@link jakarta.json.JsonReader} can also be created using {@link Json}'s
+ * {@code createReader} methods. If multiple reader instances are created,
+ * then creating them using a reader factory is preferred.
+ *
+ * <p>
+ * <b>For example:</b>
+ * <pre>
+ * <code>
+ * JsonReaderFactory factory = Json.createReaderFactory(...);
+ * JsonReader reader1 = factory.createReader(...);
+ * JsonReader reader2 = factory.createReader(...);
+ * </code>
+ * </pre>
+ *
+ * <p> All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public interface JsonReaderFactory {
+
+    /**
+     * Creates a JSON reader from a character stream. The reader is configured
+     * with the factory configuration.
+     *
+     * @param reader a reader from which JSON is to be read
+     * @return a JSON reader
+     */
+    JsonReader createReader(Reader reader);
+
+    /**
+     * Creates a JSON reader from a byte stream. The character encoding of
+     * the stream is determined as described in
+     * <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+     * The reader is configured with the factory configuration.
+     *
+     * @param in a byte stream from which JSON is to be read
+     * @return a JSON reader
+     */
+    JsonReader createReader(InputStream in);
+
+    /**
+     * Creates a JSON reader from a byte stream. The bytes of the stream
+     * are decoded to characters using the specified charset. The reader is
+     * configured with the factory configuration.
+     *
+     * @param in a byte stream from which JSON is to be read
+     * @param charset a charset
+     * @return a JSON reader
+     */
+    JsonReader createReader(InputStream in, Charset charset);
+
+    /**
+     * Returns read-only map of supported provider specific configuration
+     * properties that are used to configure the created JSON readers.
+     * If there are any specified configuration properties that are not
+     * supported by the provider, they won't be part of the returned map.
+     *
+     * @return a map of supported provider specific properties that are used
+     * to configure the readers. The map be empty but not null.
+     */
+    Map<String, ?> getConfigInUse();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonString.java b/api/src/main/java/jakarta/json/JsonString.java
new file mode 100644
index 0000000..ae833ef
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonString.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * An immutable JSON string value.
+ */
+public interface JsonString extends JsonValue {
+
+    /**
+     * Returns the JSON string value.
+     *
+     * @return a JSON string value
+     */
+    String getString();
+
+
+    /**
+     * Returns the char sequence for the JSON String value
+     *
+     * @return a char sequence for the JSON String value
+     */
+    CharSequence getChars();
+
+    /**
+     * Compares the specified object with this {@code JsonString} for equality.
+     * Returns {@code true} if and only if the specified object is also a
+     * {@code JsonString}, and their {@link #getString()} objects are
+     * <i>equal</i>.
+     *
+     * @param obj the object to be compared for equality with this 
+     *      {@code JsonString}
+     * @return {@code true} if the specified object is equal to this 
+     *      {@code JsonString}
+     */
+    @Override
+    boolean equals(Object obj);
+
+    /**
+     * Returns the hash code value for this {@code JsonString} object.  
+     * The hash code of a {@code JsonString} object is defined to be its 
+     * {@link #getString()} object's hash code.
+     *
+     * @return the hash code value for this {@code JsonString} object
+     */
+    @Override
+    int hashCode();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonStructure.java b/api/src/main/java/jakarta/json/JsonStructure.java
new file mode 100644
index 0000000..92c9c8d
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonStructure.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * Super type for the two structured types in JSON ({@link JsonObject object}s
+ * and {@link JsonArray array}s).
+ */
+public interface JsonStructure extends JsonValue {
+
+    /**
+     * Get the value referenced by the provided JSON Pointer in the JsonStructure.
+     *
+     * @param jsonPointer the JSON Pointer
+     * @return the {@code JsonValue} at the referenced location
+     * @throws JsonException if the JSON Pointer is malformed, or if it references
+     *     a non-existing member or value.
+     *
+     * @since 1.1
+     */
+    default public JsonValue getValue(String jsonPointer) {
+        return Json.createPointer(jsonPointer).getValue(this);
+    }
+}
diff --git a/api/src/main/java/jakarta/json/JsonValue.java b/api/src/main/java/jakarta/json/JsonValue.java
new file mode 100644
index 0000000..4dec772
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonValue.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+/**
+ * <code>JsonValue</code> represents an immutable JSON value.
+ *
+ *
+ * <p>A JSON value is one of the following:
+ * an object ({@link JsonObject}), an array ({@link JsonArray}),
+ * a number ({@link JsonNumber}), a string ({@link JsonString}),
+ * {@code true} ({@link JsonValue#TRUE JsonValue.TRUE}), {@code false}
+ * ({@link JsonValue#FALSE JsonValue.FALSE}),
+ * or {@code null} ({@link JsonValue#NULL JsonValue.NULL}).
+ */
+public interface JsonValue {
+
+    /**
+     * The empty JSON object.
+     *
+     * @since 1.1
+     */
+    static final JsonObject EMPTY_JSON_OBJECT = new EmptyObject();
+
+    /**
+     * The empty JSON array.
+     *
+     * @since 1.1
+     */
+    static final JsonArray EMPTY_JSON_ARRAY = new EmptyArray();
+
+    /**
+     * Indicates the type of a {@link JsonValue} object.
+     */
+    enum ValueType {
+        /**
+         * JSON array.
+         */
+        ARRAY,
+
+        /**
+         * JSON object.
+         */
+        OBJECT,
+
+        /**
+         * JSON string.
+         */
+        STRING,
+
+        /**
+         * JSON number.
+         */
+        NUMBER,
+
+        /**
+         * JSON true.
+         */
+        TRUE,
+
+        /**
+         * JSON false.
+         */
+        FALSE,
+
+        /**
+         * JSON null.
+         */
+        NULL
+    }
+
+    /**
+     * JSON null value.
+     */
+    static final JsonValue NULL = new JsonValueImpl(ValueType.NULL);
+
+    /**
+     * JSON true value.
+     */
+    static final JsonValue TRUE = new JsonValueImpl(ValueType.TRUE);
+
+    /**
+     * JSON false value.
+     */
+    static final JsonValue FALSE = new JsonValueImpl(ValueType.FALSE);
+
+    /**
+     * Returns the value type of this JSON value.
+     *
+     * @return JSON value type
+     */
+    ValueType getValueType();
+
+    /**
+     * Return the JsonValue as a JsonObject
+     *
+     * @return the JsonValue as a JsonObject
+     * @throws ClassCastException if the JsonValue is not a JsonObject
+     *
+     * @since 1.1
+     */
+    default JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    /**
+     * Return the JsonValue as a JsonArray
+     *
+     * @return the JsonValue as a JsonArray
+     * @throws ClassCastException if the JsonValue is not a JsonArray
+     *
+     * @since 1.1
+     */
+    default JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+
+    /**
+     * Returns JSON text for this JSON value.
+     *
+     * @return JSON text
+     */
+    @Override
+    String toString();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonValueImpl.java b/api/src/main/java/jakarta/json/JsonValueImpl.java
new file mode 100644
index 0000000..16d193b
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonValueImpl.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.Serializable;
+
+/**
+ * Private implementation of {@link JsonValue} for simple {@link ValueType}s
+ * allowing their usage in constants which are better to implement {@link Serializable}.
+ *
+ * @author Lukas Jungmann
+ */
+final class JsonValueImpl implements JsonValue, Serializable {
+
+    private final ValueType valueType;
+
+    JsonValueImpl(ValueType valueType) {
+        this.valueType = valueType;
+    }
+
+    /**
+     * Returns the value type of this JSON value.
+     *
+     * @return JSON value type
+     */
+    @Override
+    public ValueType getValueType() {
+        return valueType;
+    }
+
+    /**
+     * Compares the specified object with this {@link JsonValue}
+     * object for equality. Returns {@code true} if and only if the
+     * specified object is also a JsonValue, and their
+     * {@link #getValueType()} objects are <i>equal</i>.
+     *
+     * @param obj the object to be compared for equality with this JsonValue
+     * @return {@code true} if the specified object is equal to this
+     * JsonValue
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof JsonValue) {
+            return getValueType().equals(((JsonValue) obj).getValueType());
+        }
+        return false;
+    }
+
+    /**
+     * Returns the hash code value for this {@link JsonValue} object.
+     * The hash code of the {@link JsonValue} object is defined to be
+     * its {@link #getValueType()} object's hash code.
+     *
+     * @return the hash code value for this {@link JsonValue} object
+     */
+    @Override
+    public int hashCode() {
+        return valueType.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return valueType.name().toLowerCase();
+    }
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonWriter.java b/api/src/main/java/jakarta/json/JsonWriter.java
new file mode 100644
index 0000000..b20b607
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonWriter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.Closeable;
+
+/**
+ * Writes a JSON {@link JsonObject object} or {@link JsonArray array} structure
+ * to an output source.
+ *
+ * <p>The class {@link jakarta.json.Json} contains methods to create writers from
+ * output sources ({@link java.io.OutputStream} and {@link java.io.Writer}).
+ *
+ * <p>
+ * The following example demonstrates how write an empty JSON object:
+ * <pre>
+ * <code>
+ * JsonWriter jsonWriter = Json.createWriter(...);
+ * jsonWriter.writeObject(Json.createObjectBuilder().build());
+ * jsonWriter.close();
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * The class {@link JsonWriterFactory} also contains methods to create
+ * {@code JsonWriter} instances. A factory instance can be used to create
+ * multiple writer instances with the same configuration. This the preferred
+ * way to create multiple instances. A sample usage is shown in the following
+ * example:
+ * <pre>
+ * <code>
+ * JsonWriterFactory factory = Json.createWriterFactory(config);
+ * JsonWriter writer1 = factory.createWriter(...);
+ * JsonWriter writer2 = factory.createWriter(...);
+ * </code>
+ * </pre>
+ */
+public interface JsonWriter extends  /*Auto*/Closeable {
+
+    /**
+     * Writes the specified JSON {@link JsonArray array} to the output
+     * source. This method needs to be called only once for a writer instance.
+     *
+     * @param array JSON array that is to be written to the output source
+     * @throws JsonException if the specified JSON object cannot be
+     *     written due to i/o error (IOException would be cause of
+     *     JsonException)
+     * @throws IllegalStateException if writeArray, writeObject, write or close
+     *     method is already called
+     */
+    void writeArray(JsonArray array);
+
+    /**
+     * Writes the specified JSON {@link JsonObject object} to the output
+     * source. This method needs to be called only once for a writer instance.
+     *
+     * @param object JSON object that is to be written to the output source
+     * @throws JsonException if the specified JSON object cannot be
+     *     written due to i/o error (IOException would be cause of JsonException)
+     * @throws IllegalStateException if writeArray, writeObject, write or close
+     *     method is already called
+     */
+    void writeObject(JsonObject object);
+
+    /**
+     * Writes the specified JSON {@link JsonObject object} or
+     * {@link JsonArray array} to the output source. This method needs
+     * to be called only once for a writer instance.
+     *
+     * @param value JSON array or object that is to be written to the output
+     *              source
+     * @throws JsonException if the specified JSON object cannot be
+     *     written due to i/o error (IOException would be cause of
+     *     JsonException)
+     * @throws IllegalStateException if writeArray, writeObject, write
+     *     or close method is already called
+     */
+    void write(JsonStructure value);
+
+    /**
+     * Closes this JSON writer and frees any resources associated with the
+     * writer. This method closes the underlying output source.
+     *
+     * @throws JsonException if an i/o error occurs (IOException would be
+     * cause of JsonException)
+     */
+
+    /**
+     * Writes the specified {@link JsonValue} to the output source.
+     * method needs to be called only once for a write instance.
+     *
+     * @param value a {@code JsonValue} to be written to the output
+     *              source
+     * @throws JsonException if the specified JSON object cannot be
+     *     written due to i/o error (IOException would be cause of
+     *     JsonException)
+     * @throws IllegalStateException if writeArray, writeObject, write
+     *     or close method is already called
+     *
+     * @since 1.1
+     */
+    default void write(JsonValue value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    void close();
+
+}
diff --git a/api/src/main/java/jakarta/json/JsonWriterFactory.java b/api/src/main/java/jakarta/json/JsonWriterFactory.java
new file mode 100644
index 0000000..49fe62f
--- /dev/null
+++ b/api/src/main/java/jakarta/json/JsonWriterFactory.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * Factory to create {@link jakarta.json.JsonWriter} instances. If a factory
+ * instance is configured with some configuration, that would be
+ * used to configure the created writer instances.
+ *
+ * <p>
+ * {@link jakarta.json.JsonWriter} can also be created using {@link Json}'s
+ * {@code createWriter} methods. If multiple writer instances are created,
+ * then creating them using a writer factory is preferred.
+ *
+ * <p>
+ * <b>For example:</b>
+ * <pre>
+ * <code>
+ * JsonWriterFactory factory = Json.createWriterFactory(...);
+ * JsonWriter writer1 = factory.createWriter(...);
+ * JsonWriter writer2 = factory.createWriter(...);
+ * </code>
+ * </pre>
+ *
+ * <p> All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public interface JsonWriterFactory {
+
+    /**
+     * Creates a JSON writer to write a JSON {@link JsonObject object} or
+     * {@link JsonArray array} structure to the specified character stream.
+     * The writer is configured with the factory configuration.
+     *
+     * @param writer to which JSON object or array is written
+     * @return a JSON writer
+     */
+    JsonWriter createWriter(Writer writer);
+
+    /**
+     * Creates a JSON writer to write a JSON {@link JsonObject object} or
+     * {@link JsonArray array} structure to the specified byte stream.
+     * Characters written to the stream are encoded into bytes using UTF-8
+     * encoding. The writer is configured with the factory configuration.
+     *
+     * @param out to which JSON object or array is written
+     * @return a JSON writer
+     */
+    JsonWriter createWriter(OutputStream out);
+
+    /**
+     * Creates a JSON writer to write a JSON {@link JsonObject object} or
+     * {@link JsonArray array} structure to the specified byte stream.
+     * Characters written to the stream are encoded into bytes using the
+     * specified charset. The writer is configured with the factory
+     * configuration.
+     *
+     * @param out to which JSON object or array is written
+     * @param charset a charset
+     * @return a JSON writer
+     */
+    JsonWriter createWriter(OutputStream out, Charset charset);
+
+    /**
+     * Returns read-only map of supported provider specific configuration
+     * properties that are used to configure the created JSON writer objects.
+     * If there are any specified configuration properties that are not
+     * supported by the provider, they won't be part of the returned map.
+     *
+     * @return a map of supported provider specific properties that are used
+     * to configure the created writers. The map may be empty but not null.
+     */
+    Map<String, ?> getConfigInUse();
+
+}
diff --git a/api/src/main/java/jakarta/json/package-info.java b/api/src/main/java/jakarta/json/package-info.java
new file mode 100644
index 0000000..a5906ec
--- /dev/null
+++ b/api/src/main/java/jakarta/json/package-info.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Provides an object model API to process <a href="http://json.org/">JSON</a>.
+ *
+ * <p>The object model API is a high-level API that provides immutable object
+ * models for JSON object and array structures. These JSON structures are
+ * represented as object models using the Java types {@link jakarta.json.JsonObject}
+ * and {@link jakarta.json.JsonArray}. The interface {@code jakarta.json.JsonObject} provides
+ * a {@link java.util.Map} view to access the unordered collection of zero or
+ * more name/value pairs from the model. Similarly, the interface
+ * {@code JsonArray} provides a {@link java.util.List} view to access the
+ * ordered sequence of zero or more values from the model.
+ *
+ * <p>The object model API uses builder patterns to create and modify
+ * these object models. The classes {@link jakarta.json.JsonObjectBuilder} and 
+ * {@link jakarta.json.JsonArrayBuilder} provide methods to create and modify models
+ * of type {@code JsonObject} and {@code JsonArray} respectively.
+ *
+ * <p>These object models can also be created from an input source using
+ * the class {@link jakarta.json.JsonReader}. Similarly, these object models
+ * can be written to an output source using the class {@link jakarta.json.JsonWriter}.
+ * <p>
+ * This package includes several classes that implement other JSON related
+ * standards: <a href="http://tools.ietf.org/html/rfc6901">JSON Pointer</a>,
+ * <a Href="http://tools.ietf.org/html/rfc6902">JSON Patch</a>, and
+ * <a Href="http://tools.ietf.org/html/rfc7396">JSON Merge Patch</a>.
+ * They can be used to retrieve, transform or manipulate values in an
+ * object model.
+ */
+package jakarta.json;
diff --git a/api/src/main/java/jakarta/json/spi/JsonProvider.java b/api/src/main/java/jakarta/json/spi/JsonProvider.java
new file mode 100644
index 0000000..ea56275
--- /dev/null
+++ b/api/src/main/java/jakarta/json/spi/JsonProvider.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.spi;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Optional;
+
+/**
+ * Service provider for JSON processing objects.
+ *
+ * <p>All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ *
+ * @see ServiceLoader
+ */
+public abstract class JsonProvider {
+
+    /**
+     * A constant representing the name of the default
+     * {@code JsonProvider} implementation class.
+     */
+    private static final String DEFAULT_PROVIDER
+            = "org.glassfish.json.JsonProviderImpl";
+
+    protected JsonProvider() {
+    }
+
+    /**
+     * Creates a JSON provider object. The provider is loaded using the
+     * {@link ServiceLoader#load(Class)} method. If there are no available
+     * service providers, this method returns the default service provider.
+     * Users are recommended to cache the result of this method.
+     *
+     * @see ServiceLoader
+     * @return a JSON provider
+     */
+    public static JsonProvider provider() {
+        ServiceLoader<JsonProvider> loader = ServiceLoader.load(JsonProvider.class);
+        Iterator<JsonProvider> it = loader.iterator();
+        if (it.hasNext()) {
+            return it.next();
+        }
+        try {
+            Class<?> clazz = Class.forName(DEFAULT_PROVIDER);
+            return (JsonProvider) clazz.newInstance();
+        } catch (ClassNotFoundException x) {
+            throw new JsonException(
+                    "Provider " + DEFAULT_PROVIDER + " not found", x);
+        } catch (Exception x) {
+            throw new JsonException(
+                    "Provider " + DEFAULT_PROVIDER + " could not be instantiated: " + x,
+                    x);
+        }
+    }
+
+    /**
+     * Creates a JSON parser from a character stream.
+     *
+     * @param reader i/o reader from which JSON is to be read
+     * @return a JSON parser
+     */
+    public abstract JsonParser createParser(Reader reader);
+
+    /**
+     * Creates a JSON parser from the specified byte stream.
+     * The character encoding of the stream is determined
+     * as defined in <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159
+     * </a>.
+     *
+     * @param in i/o stream from which JSON is to be read
+     * @throws JsonException if encoding cannot be determined
+     *         or i/o error (IOException would be cause of JsonException)
+     * @return a JSON parser
+     */
+    public abstract JsonParser createParser(InputStream in);
+
+    /**
+     * Creates a parser factory for creating {@link JsonParser} instances.
+     *
+     * @return a JSON parser factory
+     *
+    public abstract JsonParserFactory createParserFactory();
+     */
+
+    /**
+     * Creates a parser factory for creating {@link JsonParser} instances.
+     * The factory is configured with the specified map of
+     * provider specific configuration properties. Provider implementations
+     * should ignore any unsupported configuration properties specified in
+     * the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON parsers. The map may be empty or null
+     * @return a JSON parser factory
+     */
+    public abstract JsonParserFactory createParserFactory(Map<String, ?> config);
+
+    /**
+     * Creates a JSON generator for writing JSON text to a character stream.
+     *
+     * @param writer a i/o writer to which JSON is written
+     * @return a JSON generator
+     */
+    public abstract JsonGenerator createGenerator(Writer writer);
+
+    /**
+     * Creates a JSON generator for writing JSON text to a byte stream.
+     *
+     * @param out i/o stream to which JSON is written
+     * @return a JSON generator
+     */
+    public abstract JsonGenerator createGenerator(OutputStream out);
+
+    /**
+     * Creates a generator factory for creating {@link JsonGenerator} instances.
+     *
+     * @return a JSON generator factory
+     *
+    public abstract JsonGeneratorFactory createGeneratorFactory();
+     */
+
+    /**
+     * Creates a generator factory for creating {@link JsonGenerator} instances.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should
+     * ignore any unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON generators. The map may be empty or null
+     * @return a JSON generator factory
+     */
+    public abstract JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config);
+
+    /**
+     * Creates a JSON reader from a character stream.
+     *
+     * @param reader a reader from which JSON is to be read
+     * @return a JSON reader
+     */
+    public abstract JsonReader createReader(Reader reader);
+
+    /**
+     * Creates a JSON reader from a byte stream. The character encoding of
+     * the stream is determined as described in
+     * <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+     *
+     * @param in a byte stream from which JSON is to be read
+     * @return a JSON reader
+     */
+    public abstract JsonReader createReader(InputStream in);
+
+    /**
+     * Creates a JSON writer to write a
+     * JSON {@link JsonObject object} or {@link JsonArray array}
+     * structure to the specified character stream.
+     *
+     * @param writer to which JSON object or array is written
+     * @return a JSON writer
+     */
+    public abstract JsonWriter createWriter(Writer writer);
+
+    /**
+     * Creates a JSON writer to write a
+     * JSON {@link JsonObject object} or {@link JsonArray array}
+     * structure to the specified byte stream. Characters written to
+     * the stream are encoded into bytes using UTF-8 encoding.
+     *
+     * @param out to which JSON object or array is written
+     * @return a JSON writer
+     */
+    public abstract JsonWriter createWriter(OutputStream out);
+
+    /**
+     * Creates a writer factory for creating {@link JsonWriter} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON writers. The map may be empty or null
+     * @return a JSON writer factory
+     */
+    public abstract JsonWriterFactory createWriterFactory(Map<String,?> config);
+
+    /**
+     * Creates a reader factory for creating {@link JsonReader} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON readers. The map may be empty or null
+     * @return a JSON reader factory
+     */
+    public abstract JsonReaderFactory createReaderFactory(Map<String,?> config);
+
+    /**
+     * Creates a JSON object builder.
+     *
+     * @return a JSON object builder
+     */
+    public abstract JsonObjectBuilder createObjectBuilder();
+
+    /**
+     * Creates a JSON object builder, initialized with the specified object.
+     *
+     * @param object the initial JSON object in the builder
+     * @return a JSON object builder
+     *
+     * @since 1.1
+     */
+    public JsonObjectBuilder createObjectBuilder(JsonObject object) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the data from specified {@code map}.
+     * If the @{code map} contains {@link Optional}s then resulting JSON object builder
+     * contains the key from the {@code map} only if the {@link Optional} is not empty.
+     *
+     * @param map the initial object in the builder
+     * @return a JSON object builder
+     * @exception IllegalArgumentException if the value from the {@code map} cannot be converted
+     *            to the corresponding {@link JsonValue}
+     *
+     * @since 1.1
+     */
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON array builder.
+     *
+     * @return a JSON array builder
+     */
+    public abstract JsonArrayBuilder createArrayBuilder();
+
+    /**
+     * Creates a JSON array builder, initialized with the specified array.
+     *
+     * @param array the initial JSON array in the builder
+     * @return a JSON array builder
+     *
+     * @since 1.1
+     */
+    public JsonArrayBuilder createArrayBuilder(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates JSON Pointer (<a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>)
+     * from given {@code jsonPointer} string.
+     * <ul>
+     *     <li>An empty {@code jsonPointer} string defines a reference to the target itself.</li>
+     *     <li>If the {@code jsonPointer} string is non-empty, it must be a sequence of '{@code /}' prefixed tokens.</li>
+     * </ul>
+     *
+     * @param jsonPointer the JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer
+     * @return a JSON Pointer
+     *
+     * @since 1.1
+     */
+    public JsonPointer createPointer(String jsonPointer) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON Patch builder (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>).
+     *
+     * @return a JSON Patch builder
+     *
+     * @since 1.1
+     */
+    public JsonPatchBuilder createPatchBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON Patch builder
+     * (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>),
+     * initialized with the specified operations.
+     *
+     * @param array the initial patch operations
+     * @return a JSON Patch builder
+     *
+     * @since 1.1
+     */
+    public JsonPatchBuilder createPatchBuilder(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON Patch (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>)
+     * from the specified operations.
+     *
+     * @param array patch operations
+     * @return a JSON Patch
+     *
+     * @since 1.1
+     */
+    public JsonPatch createPatch(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Generates a JSON Patch (<a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>)
+     * from the source and target {@code JsonStructure}.
+     * The generated JSON Patch need not be unique.
+     *
+     * @param source the source
+     * @param target the target, must be the same type as the source
+     * @return a JSON Patch which when applied to the source, yields the target
+     *
+     * @since 1.1
+     */
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from specified {@code JsonValue}.
+     *
+     * @param patch the patch
+     * @return a JSON Merge Patch
+     *
+     * @since 1.1
+     */
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Generates a JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from the source and target {@code JsonValue}s
+     * which when applied to the {@code source}, yields the {@code target}.
+     *
+     * @param source the source
+     * @param target the target
+     * @return a JSON Merge Patch
+     *
+     * @since 1.1
+     */
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with the content of specified {@code collection}.
+     * If the @{code collection} contains {@link Optional}s then resulting JSON array builder
+     * contains the value from the {@code collection} only if the {@link Optional} is not empty.
+     *
+     * @param collection the initial data for the builder
+     * @return a JSON array builder
+     * @exception IllegalArgumentException if the value from the {@code collection} cannot be converted
+     *            to the corresponding {@link JsonValue}
+     *
+     * @since 1.1
+     */
+    public JsonArrayBuilder createArrayBuilder(Collection<?> collection) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Creates a builder factory for creating {@link JsonArrayBuilder}
+     * and {@link JsonObjectBuilder} objects.
+     * The factory is configured with the specified map of provider specific
+     * configuration properties. Provider implementations should ignore any
+     * unsupported configuration properties specified in the map.
+     *
+     * @param config a map of provider specific properties to configure the
+     *               JSON builders. The map may be empty or null
+     * @return a JSON builder factory
+     */
+    public abstract JsonBuilderFactory createBuilderFactory(Map<String,?> config);
+
+    /**
+     * Creates a JsonString.
+     *
+     * @param value a JSON string
+     * @return the JsonString for the string
+     *
+     * @since 1.1
+     */
+    public JsonString createValue(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public JsonNumber createValue(int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public JsonNumber createValue(long value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public JsonNumber createValue(double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public JsonNumber createValue(BigDecimal value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a JsonNumber.
+     *
+     * @param value a JSON number
+     * @return the JsonNumber for the number
+     *
+     * @since 1.1
+     */
+    public JsonNumber createValue(BigInteger value) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/api/src/main/java/jakarta/json/spi/package-info.java b/api/src/main/java/jakarta/json/spi/package-info.java
new file mode 100644
index 0000000..57c892b
--- /dev/null
+++ b/api/src/main/java/jakarta/json/spi/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Service Provider Interface (SPI) to plug in implementations for
+ * JSON processing objects.
+ *
+ * <p> {@link jakarta.json.spi.JsonProvider JsonProvider} is an abstract class 
+ * that provides a service for creating JSON processing instances.
+ * A <i>service provider</i> for {@code JsonProvider} provides an 
+ * specific implementation by subclassing and implementing the methods in
+ * {@code JsonProvider}. This enables using custom, efficient JSON processing
+ * implementations (for e.g. parser and generator) other than the default ones.
+ *
+ * <p>The API locates and loads providers using {@link java.util.ServiceLoader}.
+ *
+ * @since JSON Processing 1.0
+ */
+package jakarta.json.spi;
diff --git a/api/src/main/java/jakarta/json/stream/JsonCollectors.java b/api/src/main/java/jakarta/json/stream/JsonCollectors.java
new file mode 100644
index 0000000..2a1678d
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonCollectors.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.stream.Collector;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.BiConsumer;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonException;
+
+/**
+ * This class contains some implementations of {@code java.util.stream.Collector} for accumulating
+ * {@link JsonValue}s into {@link JsonArray} and {@link JsonObject}.
+ *
+ * @since 1.1
+ */
+
+public final class JsonCollectors {
+
+    private JsonCollectors() {
+    }
+
+    /**
+     * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code JsonValue}
+     * elements into a {@code JsonArray}.
+     *
+     * @return the constructed Collector
+     */
+    public static Collector<JsonValue, JsonArrayBuilder, JsonArray> toJsonArray() {
+        return Collector.of(
+                Json::createArrayBuilder,
+                JsonArrayBuilder::add,
+                JsonArrayBuilder::addAll,
+                JsonArrayBuilder::build);
+    }
+
+    /**
+     * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code Map.Entry<String,JsonValue>}
+     * elements into a {@code JsonObject}.
+     *
+     * @return the constructed Collector
+     */
+    public static Collector<Map.Entry<String, JsonValue>, JsonObjectBuilder, JsonObject> toJsonObject() {
+        return Collector.of(
+                Json::createObjectBuilder,
+                (JsonObjectBuilder b, Map.Entry<String, JsonValue> v) -> b.add(v.getKey(), v.getValue()),
+                JsonObjectBuilder::addAll,
+                JsonObjectBuilder::build);
+    }
+
+    /**
+     * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code JsonValue}
+     * elements into a {@code JsonObject}.  The name/value pairs of the {@code JsonObject} are computed
+     * by applying the provided mapping functions.
+     *
+     * @param keyMapper a mapping function to produce names.
+     * @param valueMapper a mapping function to produce values
+     * @return the constructed Collector
+     */
+    public static Collector<JsonValue, JsonObjectBuilder, JsonObject>
+                toJsonObject(Function<JsonValue, String> keyMapper,
+                             Function<JsonValue, JsonValue> valueMapper) {
+        return Collector.of(
+                Json::createObjectBuilder,
+                (b, v) -> b.add(keyMapper.apply(v), valueMapper.apply(v)),
+                JsonObjectBuilder::addAll,
+                JsonObjectBuilder::build,
+                Collector.Characteristics.UNORDERED);
+    }
+
+    /**
+     * Constructs a {@code java.util.stream.Collector} that implements a "group by" operation on the
+     * input {@code JsonValue} elements. A classifier function maps the input {@code JsonValue}s to keys, and
+     * the {@code JsonValue}s are partitioned into groups according to the value of the key.
+     * A reduction operation is performed on the {@code JsonValue}s in each group, using the
+     * downstream {@code Collector}. For each group, the key and the results of the reduction operation
+     * become the name/value pairs of the resultant {@code JsonObject}.
+     *
+     * @param <T> the intermediate accumulation {@code JsonArrayBuilder} of the downstream collector
+     * @param classifier a function mapping the input {@code JsonValue}s to a String, producing keys
+     * @param downstream a {@code Collector} that implements a reduction operation on the
+     *        {@code JsonValue}s in each group.
+     * @return the constructed {@code Collector}
+     */
+    public static <T extends JsonArrayBuilder> Collector<JsonValue, Map<String, T>, JsonObject>
+                groupingBy(Function<JsonValue, String> classifier,
+                           Collector<JsonValue, T, JsonArray> downstream) {
+
+        BiConsumer<Map<String, T>, JsonValue> accumulator =
+            (map, value) -> {
+                String key = classifier.apply(value);
+                if (key == null) {
+                    throw new JsonException("element cannot be mapped to a null key");
+                }
+                // Build a map of key to JsonArrayBuilder
+                T arrayBuilder =
+                    map.computeIfAbsent(key, v->downstream.supplier().get());
+                // Add elements from downstream Collector to the arrayBuilder.
+                downstream.accumulator().accept(arrayBuilder, value);
+            };
+        Function<Map<String, T>, JsonObject> finisher =
+            map -> {
+                // transform the map of name: JsonArrayBuilder to
+                //                      name: JsonArray
+                // using the downstream collector for reducing the JsonArray
+                JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+                map.forEach((k, v) -> {
+                    JsonArray array = downstream.finisher().apply(v);
+                    objectBuilder.add(k, array);
+                });
+                return objectBuilder.build();
+            };
+        BinaryOperator<Map<String, T>> combiner =
+            (map1, map2) -> {
+                map1.putAll(map2);
+                return map1;
+            };
+        return Collector.of(HashMap::new, accumulator, combiner, finisher,
+            Collector.Characteristics.UNORDERED);
+    }
+
+    /**
+     * Constructs a {@code java.util.stream.Collector} that implements a "group by" operation on the
+     * input {@code JsonValue} elements. A classifier function maps the input {@code JsonValue}s to keys, and
+     * the {@code JsonValue}s are partitioned into groups according to the value of the key.
+     * The {@code JsonValue}s in each group are added to a {@code JsonArray}.  The key and the
+     * {@code JsonArray} in each group becomes the name/value pair of the resultant {@code JsonObject}.
+     *
+     * @param classifier a function mapping the input {@code JsonValue}s to a String, producing keys
+     * @return the constructed {@code Collector}
+     */
+    public static Collector<JsonValue, Map<String, JsonArrayBuilder>, JsonObject>
+                groupingBy(Function<JsonValue, String> classifier) {
+        return groupingBy(classifier, toJsonArray());
+    }
+}
+
diff --git a/api/src/main/java/jakarta/json/stream/JsonGenerationException.java b/api/src/main/java/jakarta/json/stream/JsonGenerationException.java
new file mode 100644
index 0000000..168d673
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonGenerationException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import jakarta.json.JsonException;
+
+/**
+ * {@code JsonGenerationException} indicates an incorrect JSON is
+ * being generated.
+ */
+public class JsonGenerationException extends JsonException {
+
+    /**
+     * Constructs a new runtime exception with the specified detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public JsonGenerationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this runtime exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method). (A <code>null</code> value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     */
+    public JsonGenerationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
+
diff --git a/api/src/main/java/jakarta/json/stream/JsonGenerator.java b/api/src/main/java/jakarta/json/stream/JsonGenerator.java
new file mode 100644
index 0000000..7ca3d3f
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonGenerator.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import jakarta.json.JsonValue;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Writes JSON data to an output source in a streaming way. The class
+ * {@link jakarta.json.Json} contains methods to create generators for character
+ * or output streams ({@link java.io.Writer} and {@link java.io.OutputStream}).
+ *
+ * <p>
+ * The following example shows how to create a JSON generator:
+ * <pre>
+ * <code>
+ * JsonGenerator generator = Json.createGenerator(...);
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * The class {@link JsonGeneratorFactory} also contains methods to create
+ * {@code JsonGenerator} instances. {@link JsonGeneratorFactory} should be used
+ * when creating multiple generator instances, as in the following example:
+ * <pre>
+ * <code>
+ * JsonGeneratorFactory factory = Json.createGeneratorFactory();
+ * JsonGenerator generator1 = factory.createGenerator(...);
+ * JsonGenerator generator2 = factory.createGenerator(...);
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * JSON objects can be created using {@code JsonGenerator} by calling the
+ * {@link #writeStartObject()} method and then adding name/value pairs with the
+ * {@code write} method.
+ * <p>
+ * The following example shows how to generate an empty JSON object:
+ * <pre>
+ * <code>
+ * JsonGenerator generator = ...;
+ * generator.writeStartObject().writeEnd().close();
+ * </code>
+ * </pre>
+ *
+ * JSON arrays can be created using {@code JsonGenerator} by calling the
+ * {@link #writeStartArray()} method and then adding values with the
+ * {@code write} method.
+ *
+ * <p>
+ * The following example shows how to generate an empty JSON array:
+ * <pre>
+ * <code>
+ * JsonGenerator generator = ...;
+ * generator.writeStartArray().writeEnd().close();
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * Other JSON values (that are not JSON objects or arrays) can be created
+ * by calling the appropiate {@code write} methods.
+ * <p>
+ * The following example shows how to generate a JSON string:
+ * <pre><code>
+ * JsonGenerator generator = ...;
+ * generator.write("message").close();
+ * </code></pre>
+ *
+ * {@code JsonGenerator} methods can be chained as in the following example:
+ * <pre>
+ * <code>
+ * generator
+ *     .writeStartObject()
+ *         .write("firstName", "John")
+ *         .write("lastName", "Smith")
+ *         .write("age", 25)
+ *         .writeStartObject("address")
+ *             .write("streetAddress", "21 2nd Street")
+ *             .write("city", "New York")
+ *             .write("state", "NY")
+ *             .write("postalCode", "10021")
+ *         .writeEnd()
+ *         .writeStartArray("phoneNumber")
+ *             .writeStartObject()
+ *                 .write("type", "home")
+ *                 .write("number", "212 555-1234")
+ *             .writeEnd()
+ *             .writeStartObject()
+ *                 .write("type", "fax")
+ *                 .write("number", "646 555-4567")
+ *             .writeEnd()
+ *         .writeEnd()
+ *     .writeEnd();
+ * generator.close();
+ * </code>
+ * </pre>
+ *
+ * The example code above generates the following JSON (or equivalent):
+ * <pre>
+ * <code>
+ * {
+ *   "firstName": "John", "lastName": "Smith", "age": 25,
+ *   "address" : {
+ *       "streetAddress": "21 2nd Street",
+ *       "city": "New York",
+ *       "state": "NY",
+ *       "postalCode": "10021"
+ *   },
+ *   "phoneNumber": [
+ *       {"type": "home", "number": "212 555-1234"},
+ *       {"type": "fax", "number": "646 555-4567"}
+ *    ]
+ * }
+ * </code>
+ * </pre>
+ *
+ * The generated JSON text must strictly conform to the grammar defined in
+ * <a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+ *
+ * @see jakarta.json.Json
+ * @see JsonGeneratorFactory
+ */
+public interface JsonGenerator extends Flushable, /*Auto*/Closeable {
+    /**
+     * Configuration property to generate JSON prettily. All providers
+     * must support this property. The value of the property could be
+     * be anything.
+     */
+    String PRETTY_PRINTING = "jakarta.json.stream.JsonGenerator.prettyPrinting" ;
+
+    /**
+     * Writes the JSON start object character. It starts a new child object
+     * context within which JSON name/value pairs can be written to the object.
+     * This method is valid only in an array context, field context or in no context (when a
+     * context is not yet started). This method can only be called once in
+     * no context.
+     *
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is called within an
+     *      object context or if it is called more than once in no context.
+     */
+    JsonGenerator writeStartObject();
+
+    /**
+     * Writes the JSON name/start object character pair in the current
+     * object context. It starts a new child object context within which JSON
+     * name/value pairs can be written to the object.
+     *
+     * @param name a name within the JSON name/object pair to be written
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *     object context
+     */
+    JsonGenerator writeStartObject(String name);
+
+    /**
+     * Writes the JSON name with a colon. It starts a field context, in which valid
+     * options are writing a value, starting an object or an array.
+     *
+     * Writing value closes field context, if object or array is started after field name,
+     * field context will be closed after object/array close.
+     *
+     * @param name name of json field
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *     object context
+     *
+     * @since 1.1
+     */
+    JsonGenerator writeKey(String name);
+
+    /**
+     * Writes the JSON start array character. It starts a new child array
+     * context within which JSON values can be written to the array. This
+     * method is valid only in an array context, field context or in no context (when a
+     * context is not yet started). This method can only be called once in
+     * no context.
+     *
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is called within an
+     *      object context or if called more than once in no context
+     */
+    JsonGenerator writeStartArray();
+
+    /**
+     * Writes the JSON name/start array character pair with in the current
+     * object context. It starts a new child array context within which JSON
+     * values can be written to the array.
+     *
+     * @param name a name within the JSON name/array pair to be written
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within
+     *      an object context
+     */
+    JsonGenerator writeStartArray(String name);
+
+    /**
+     * Writes a JSON name/value pair in the current object context.
+     *
+     * @param name a name in the JSON name/value pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/value pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context
+     */
+    JsonGenerator write(String name, JsonValue value);
+
+    /**
+     * Writes a JSON name/string value pair in the current object context.
+     * The specified value is written as JSON string value.
+     *
+     * @param name a name in the JSON name/string pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/string pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context
+     */
+    JsonGenerator write(String name, String value);
+
+    /**
+     * Writes a JSON name/number value pair in the current object context.
+     * The specified value is written as a JSON number value. The string
+     * {@code new BigDecimal(value).toString()}
+     * is used as the text value for writing.
+     *
+     * @param name a name in the JSON name/number pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/number pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context.
+     */
+    JsonGenerator write(String name, BigInteger value);
+
+    /**
+     * Writes a JSON name/number value pair in the current object context.
+     * The specified value is written as a JSON number value. The specified
+     * value's {@code toString()} is used as the text value for writing.
+     *
+     * @param name a name in the JSON name/number pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/number pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context.
+     */
+    JsonGenerator write(String name, BigDecimal value);
+
+    /**
+     * Writes a JSON name/number value pair in the current object context.
+     * The specified value is written as a JSON number value. The string
+     * {@code new BigDecimal(value).toString()} is used as the text value
+     * for writing.
+     *
+     * @param name a name in the JSON name/number pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/number pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context.
+     */
+    JsonGenerator write(String name, int value);
+
+    /**
+     * Writes a JSON name/number value pair in the current object context.
+     * The specified value is written as a JSON number value. The string
+     * {@code new BigDecimal(value).toString()} is used as the text
+     * value for writing.
+     *
+     * @param name a name in the JSON name/number pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/number pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context.
+     */
+    JsonGenerator write(String name, long value);
+
+    /**
+     * Writes a JSON name/number value pair in the current object context.
+     * The specified value is written as a JSON number value. The string
+     * {@code BigDecimal.valueOf(double).toString()}
+     * is used as the text value for writing.
+     *
+     * @param name a name in the JSON name/number pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/number pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or infinity.
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context
+     */
+    JsonGenerator write(String name, double value);
+
+    /**
+     * Writes a JSON name/boolean value pair in the current object context.
+     * If value is true, it writes the JSON {@code true} value, otherwise
+     * it writes the JSON {@code false} value.
+     *
+     * @param name a name in the JSON name/boolean pair to be written in
+     *             current JSON object
+     * @param value a value in the JSON name/boolean pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context.
+     */
+    JsonGenerator write(String name, boolean value);
+
+
+    /**
+     * Writes a JSON name/null value pair in an current object context.
+     *
+     * @param name a name in the JSON name/null pair to be written in
+     *             current JSON object
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      object context
+     */
+    JsonGenerator writeNull(String name);
+
+    /**
+     * Writes the end of the current context. If the current context is
+     * an array context, this method writes the end-of-array character (']').
+     * If the current context is an object context, this method writes the
+     * end-of-object character ('}'). After writing the end of the current
+     * context, the parent context becomes the new current context.
+     * If parent context is field context, it is closed.
+     *
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is called in no context.
+     */
+    JsonGenerator writeEnd();
+
+    /**
+     * Writes the specified value as a JSON value within
+     * the current array, field or root context.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator write(JsonValue value);
+
+    /**
+     * Writes the specified value as a JSON string value within
+     * the current array, field or root context.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator write(String value);
+
+    /**
+     * Writes the specified value as a JSON number value within
+     * the current array, field or root context. The specified value's {@code toString()}
+     * is used as the the text value for writing.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     *
+     * @see jakarta.json.JsonNumber
+     */
+    JsonGenerator write(BigDecimal value);
+
+    /**
+     * Writes the specified value as a JSON number value within
+     * the current array, field or root context. The string {@code new BigDecimal(value).toString()}
+     * is used as the text value for writing.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator.
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     *
+     * @see jakarta.json.JsonNumber
+     */
+    JsonGenerator write(BigInteger value);
+
+    /**
+     * Writes the specified value as a JSON number value within
+     * the current array, field or root context. The string {@code new BigDecimal(value).toString()}
+     * is used as the text value for writing.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator write(int value);
+
+    /**
+     * Writes the specified value as a JSON number value within
+     * the current array, field or root context. The string {@code new BigDecimal(value).toString()}
+     * is used as the text value for writing.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator write(long value);
+
+    /**
+     * Writes the specified value as a JSON number value within the current
+     * array, field or root context. The string {@code BigDecimal.valueOf(value).toString()}
+     * is used as the text value for writing.
+     *
+     * @param value a value to be written in current JSON array
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     * @throws NumberFormatException if the value is Not-a-Number (NaN) or infinity.
+     */
+    JsonGenerator write(double value);
+
+    /**
+     * Writes a JSON true or false value within the current array, field or root context.
+     * If value is true, this method writes the JSON {@code true} value,
+     * otherwise it writes the JSON {@code false} value.
+     *
+     * @param value a {@code boolean} value
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator write(boolean value);
+
+    /**
+     * Writes a JSON null value within the current array, field or root context.
+     *
+     * @return this generator
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if this method is not called within an
+     *      array or root context.
+     */
+    JsonGenerator writeNull();
+
+    /**
+     * Closes this generator and frees any resources associated with it.
+     * This method closes the underlying output source.
+     *
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonGenerationException if an incomplete JSON is generated
+     */
+    @Override
+    void close();
+
+    /**
+     * Flushes the underlying output source. If the generator has saved
+     * any characters in a buffer, writes them immediately to the underlying
+     * output source before flushing it.
+     *
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     */
+    @Override
+    void flush();
+
+}
diff --git a/api/src/main/java/jakarta/json/stream/JsonGeneratorFactory.java b/api/src/main/java/jakarta/json/stream/JsonGeneratorFactory.java
new file mode 100644
index 0000000..2483e7a
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonGeneratorFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * Factory to create {@link JsonGenerator} instances. If a factory
+ * instance is configured with some configuration, the configuration applies
+ * to all generator instances created using that factory instance.
+ *
+ * <p>
+ * The class {@link jakarta.json.Json Json} also provides methods to create
+ * {@link JsonGenerator} instances, but using {@code JsonGeneratorFactory} is
+ * preferred when creating multiple generator instances as shown in the
+ * following example:
+ *
+ * <pre>
+ * <code>
+ * JsonGeneratorFactory factory = Json.createGeneratorFactory();
+ * JsonGenerator generator1 = factory.createGenerator(...);
+ * JsonGenerator generator2 = factory.createGenerator(...);
+ * </code>
+ * </pre>
+ *
+ * <p> All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public interface JsonGeneratorFactory {
+
+    /**
+     * Creates a JSON generator to write JSON text to a character stream.
+     * The generator is configured with the factory configuration.
+     *
+     * @param writer i/o writer to which JSON is written
+     * @return the created JSON generator
+     */
+    JsonGenerator createGenerator(Writer writer);
+
+    /**
+     * Creates a JSON generator to write JSON text to a byte stream. Characters 
+     * written to the stream are encoded into bytes using UTF-8 encoding. 
+     * The generator is configured with the factory's configuration.
+     *
+     * @param out i/o stream to which JSON is written
+     * @return the created JSON generator
+     */
+    JsonGenerator createGenerator(OutputStream out);
+
+    /**
+     * Creates a JSON generator to write JSON text to a byte stream. Characters 
+     * written to the stream are encoded into bytes using the specified charset. 
+     * The generator is configured with the factory's configuration.
+     *
+     * @param out i/o stream to which JSON is written
+     * @param charset a charset
+     * @return the created JSON generator
+     */
+    JsonGenerator createGenerator(OutputStream out, Charset charset);
+
+    /**
+     * Returns a read-only map of supported provider specific configuration
+     * properties that are used to configure the JSON generators.
+     * If there are any specified configuration properties that are not
+     * supported by the provider, they won't be part of the returned map.
+     *
+     * @return a map of supported provider specific properties that are used
+     * to configure the created generators. The map may be empty but not null
+     */
+    Map<String, ?> getConfigInUse();
+
+}
diff --git a/api/src/main/java/jakarta/json/stream/JsonLocation.java b/api/src/main/java/jakarta/json/stream/JsonLocation.java
new file mode 100644
index 0000000..3bf5567
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonLocation.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+/**
+ * Provides the location information of a JSON event in an input source. The
+ * {@code JsonLocation} information can be used to identify incorrect JSON
+ * or can be used by higher frameworks to know about the processing location.
+ *
+ * <p>All the information provided by a {@code JsonLocation} is optional. For
+ * example, a provider may only report line numbers. Also, there may not be any
+ * location information for an input source. For example, if a
+ * {@code JsonParser} is created using
+ * {@link jakarta.json.JsonArray JsonArray} input source, all the methods in
+ * this class return -1.
+ * @see JsonParser
+ * @see JsonParsingException
+ */
+public interface JsonLocation {
+
+    /**
+     * Return the line number (starts with 1 for the first line) for the current JSON event in the input source.
+     *
+     * @return the line number (starts with 1 for the first line) or -1 if none is available
+     */
+    long getLineNumber();
+
+    /**
+     * Return the column number (starts with 1 for the first column) for the current JSON event in the input source.
+     *
+     * @return the column number (starts with 1 for the first column) or -1 if none is available
+     */
+    long getColumnNumber();
+
+    /**
+     * Return the stream offset into the input source this location
+     * is pointing to. If the input source is a file or a byte stream then
+     * this is the byte offset into that stream, but if the input source is
+     * a character media then the offset is the character offset.
+     * Returns -1 if there is no offset available.
+     *
+     * @return the offset of input source stream, or -1 if there is
+     * no offset available
+     */
+    long getStreamOffset();
+
+}
diff --git a/api/src/main/java/jakarta/json/stream/JsonParser.java b/api/src/main/java/jakarta/json/stream/JsonParser.java
new file mode 100644
index 0000000..e455a6e
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonParser.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+
+import java.io.Closeable;
+import java.math.BigDecimal;
+import java.util.stream.Stream;
+import java.util.Map;
+
+import jakarta.json.JsonValue;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonArray;
+
+/**
+ * Provides forward, read-only access to JSON data in a streaming way. This
+ * is the most efficient way for reading JSON data.
+ * This is the only way to parse and process JSON data that are too big to be loaded in memory.
+ * <p>The class
+ * {@link jakarta.json.Json} contains methods to create parsers from input
+ * sources ({@link java.io.InputStream} and {@link java.io.Reader}).
+ *
+ * <p>
+ * The following example demonstrates how to create a parser from a string
+ * that contains an empty JSON array:
+ * <pre>
+ * <code>
+ * JsonParser parser = Json.createParser(new StringReader("[]"));
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * The class {@link JsonParserFactory} also contains methods to create
+ * {@code JsonParser} instances. {@link JsonParserFactory} is preferred
+ * when creating multiple parser instances. A sample usage is shown
+ * in the following example:
+ * <pre>
+ * <code>
+ * JsonParserFactory factory = Json.createParserFactory();
+ * JsonParser parser1 = factory.createParser(...);
+ * JsonParser parser2 = factory.createParser(...);
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * {@code JsonParser} parses JSON using the pull parsing programming model.
+ * In this model the client code controls the thread and calls the method
+ * {@code next()} to advance the parser to the next state after
+ * processing each element. The parser can generate the following events:
+ * {@code START_OBJECT}, {@code END_OBJECT}, {@code START_ARRAY},
+ * {@code END_ARRAY}, {@code KEY_NAME}, {@code VALUE_STRING},
+ * {@code VALUE_NUMBER}, {@code VALUE_TRUE}, {@code VALUE_FALSE},
+ * and {@code VALUE_NULL}.
+ *
+ * <p>
+ * <b>For example</b>, for an empty JSON object ({ }), the parser generates the event
+ * {@code START_OBJECT} with the first call to the method {@code next()} and the
+ * event {@code END_OBJECT} with the second call to the method {@code next()}.
+ * The following code demonstrates how to access these events:
+ *
+ * <pre>
+ * <code>
+ * Event event = parser.next(); // START_OBJECT
+ * event = parser.next();       // END_OBJECT
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * <b>For example</b>, for the following JSON:
+ * <pre>
+ * {
+ *   "firstName": "John", "lastName": "Smith", "age": 25,
+ *   "phoneNumber": [
+ *       { "type": "home", "number": "212 555-1234" },
+ *       { "type": "fax", "number": "646 555-4567" }
+ *    ]
+ * }
+ * </pre>
+ *
+ * <p>calls to the method {@code next()} result in parse events at the specified
+ * locations below (marked in bold):
+ *
+ * <pre>
+ * {<B>START_OBJECT</B>
+ *   "firstName"<B>KEY_NAME</B>: "John"<B>VALUE_STRING</B>, "lastName"<B>KEY_NAME</B>: "Smith"<B>VALUE_STRING</B>, "age"<B>KEY_NAME</B>: 25<B>VALUE_NUMBER</B>,
+ *   "phoneNumber"<B>KEY_NAME</B> : [<B>START_ARRAY</B>
+ *       {<B>START_OBJECT</B> "type"<B>KEY_NAME</B>: "home"<B>VALUE_STRING</B>, "number"<B>KEY_NAME</B>: "212 555-1234"<B>VALUE_STRING</B> }<B>END_OBJECT</B>,
+ *       {<B>START_OBJECT</B> "type"<B>KEY_NAME</B>: "fax"<B>VALUE_STRING</B>, "number"<B>KEY_NAME</B>: "646 555-4567"<B>VALUE_STRING</B> }<B>END_OBJECT</B>
+ *    ]<B>END_ARRAY</B>
+ * }<B>END_OBJECT</B>
+ * </pre>
+ *
+ * The methods {@link #next()} and {@link #hasNext()} enable iteration over
+ * parser events to process JSON data. {@code JsonParser} provides get methods
+ * to obtain the value at the current state of the parser. For example, the
+ * following code shows how to obtain the value "John" from the JSON above:
+ *
+ * <pre>
+ * <code>
+ * Event event = parser.next(); // START_OBJECT
+ * event = parser.next();       // KEY_NAME
+ * event = parser.next();       // VALUE_STRING
+ * parser.getString();          // "John"
+ * </code>
+ * </pre>
+ *
+ * Starting in version 1.1, it is possible to build a partial JSON object
+ * model from the stream, at the current parser position.
+ * The methods {@link #getArray} and {@link #getObject} can be used to read in
+ * a {@code JsonArray} or {@code JsonObject}.  For example, the following code
+ * shows how to obtain the phoneNumber in a JsonArray, from the JSON above:
+ *
+ * <pre><code>
+ * while (parser.hasNext() {
+ *     Event event = parser.next();
+ *     if (event == JsonParser.Event.KEY_NAME ) {
+ *         String key = getString();
+ *         event = parser.next();
+ *         if (key.equals("phoneNumber") {
+ *             JsonArray phones = parser.getArray();
+ *         }
+ *     }
+ * }
+ * </code></pre>
+ *
+ * The methods {@link #getArrayStream} and {@link #getObjectStream} can be used
+ * to get a stream of the elements of a {@code JsonArray} or {@code JsonObject}.
+ * For example, the following code shows another way to obtain John's phoneNumber
+ * in a {@code JsonArray} :
+ *
+ * <pre>{@code
+ * Event event = parser.next(); // START_OBJECT
+ * JsonArray phones = (JsonArray)
+ *     parser.getObjectStream().filter(e->e.getKey().equals("phoneNumber"))
+ *                             .map(e->e.getValue())
+ *                             .findFirst()
+ *                             .get();
+ * }</pre>
+ *
+ * The methods {@link #skipArray} and {@link #skipObject} can be used to
+ * skip tokens and position the parser to {@code END_ARRAY} or
+ * {@code END_OBJECT}.
+ * <p>
+ * {@code JsonParser} can be used to parse sequence of JSON values that are not
+ * enclosed in a JSON array, e.g. { } { }. The following code demonstrates how
+ * to parse such sequence.
+ * <pre><code>
+ * JsonParser parser = Json.createParser(...);
+ * while (parser.hasNext) {
+ *     parser.next(); // advance parser state
+ *     JsonValue value = parser.getValue();
+ * }
+ * </code></pre>
+ *
+ * @see jakarta.json.Json
+ * @see JsonParserFactory
+ */
+public interface JsonParser extends /*Auto*/Closeable {
+
+    /**
+     * An event from {@code JsonParser}.
+     */
+    enum Event {
+        /**
+         * Start of a JSON array. The position of the parser is after '['.
+         */
+        START_ARRAY,
+        /**
+         * Start of a JSON object. The position of the parser is after '{'.
+         */
+        START_OBJECT,
+        /**
+         * Name in a name/value pair of a JSON object. The position of the parser
+         * is after the key name. The method {@link #getString} returns the key
+         * name.
+         */
+        KEY_NAME,
+        /**
+         * String value in a JSON array or object. The position of the parser is
+         * after the string value. The method {@link #getString}
+         * returns the string value.
+         */
+        VALUE_STRING,
+        /**
+         * Number value in a JSON array or object. The position of the parser is
+         * after the number value. {@code JsonParser} provides the following
+         * methods to access the number value: {@link #getInt},
+         * {@link #getLong}, and {@link #getBigDecimal}.
+         */
+        VALUE_NUMBER,
+        /**
+         * {@code true} value in a JSON array or object. The position of the
+         * parser is after the {@code true} value.
+         */
+        VALUE_TRUE,
+        /**
+         * {@code false} value in a JSON array or object. The position of the
+         * parser is after the {@code false} value.
+         */
+        VALUE_FALSE,
+        /**
+         * {@code null} value in a JSON array or object. The position of the
+         * parser is after the {@code null} value.
+         */
+        VALUE_NULL,
+        /**
+         * End of a JSON object. The position of the parser is after '}'.
+         */
+        END_OBJECT,
+        /**
+         * End of a JSON array. The position of the parser is after ']'.
+         */
+        END_ARRAY
+    }
+
+    /**
+     * Returns {@code true} if there are more parsing states. This method returns
+     * {@code false} if the parser reaches the end of the JSON text.
+     *
+     * @return {@code true} if there are more parsing states.
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonParsingException if the parser encounters invalid JSON
+     * when advancing to next state.
+     */
+    boolean hasNext();
+
+    /**
+     * Returns the event for the next parsing state.
+     *
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     * @throws JsonParsingException if the parser encounters invalid JSON
+     * when advancing to next state.
+     * @throws java.util.NoSuchElementException if there are no more parsing
+     * states.
+     * @return the event for the next parsing state
+     */
+    Event next();
+
+    /**
+     * Returns a {@code String} for the name in a name/value pair,
+     * for a string value or a number value. This method should only be called
+     * when the parser state is {@link Event#KEY_NAME}, {@link Event#VALUE_STRING},
+     * or {@link Event#VALUE_NUMBER}.
+     *
+     * @return a name when the parser state is {@link Event#KEY_NAME}
+     *         a string value when the parser state is {@link Event#VALUE_STRING}
+     *         a number value when the parser state is {@link Event#VALUE_NUMBER}
+     * @throws IllegalStateException when the parser state is not
+     *      {@code KEY_NAME}, {@code VALUE_STRING}, or {@code VALUE_NUMBER}
+     */
+    String getString();
+
+    /**
+     * Returns true if the JSON number at the current parser state is a
+     * integral number. A {@link BigDecimal} may be used to store the value
+     * internally and this method semantics are defined using its
+     * {@code scale()}. If the scale is zero, then it is considered integral
+     * type. This integral type information can be used to invoke an
+     * appropriate accessor method to obtain a numeric value as in the
+     * following example:
+     *
+     * <pre>
+     * <code>
+     * JsonParser parser = ...
+     * if (parser.isIntegralNumber()) {
+     *     parser.getInt();     // or other methods to get integral value
+     * } else {
+     *     parser.getBigDecimal();
+     * }
+     * </code>
+     * </pre>
+     *
+     * @return true if this number is a integral number, otherwise false
+     * @throws IllegalStateException when the parser state is not
+     *      {@code VALUE_NUMBER}
+     */
+    boolean isIntegralNumber();
+
+    /**
+     * Returns a JSON number as an integer. The returned value is equal
+     * to {@code new BigDecimal(getString()).intValue()}. Note that
+     * this conversion can lose information about the overall magnitude
+     * and precision of the number value as well as return a result with
+     * the opposite sign. This method should only be called when the parser
+     * state is {@link Event#VALUE_NUMBER}.
+     *
+     * @return an integer for a JSON number
+     * @throws IllegalStateException when the parser state is not
+     *      {@code VALUE_NUMBER}
+     * @see java.math.BigDecimal#intValue()
+     */
+    int getInt();
+
+    /**
+     * Returns a JSON number as a long. The returned value is equal
+     * to {@code new BigDecimal(getString()).longValue()}. Note that this
+     * conversion can lose information about the overall magnitude and
+     * precision of the number value as well as return a result with
+     * the opposite sign. This method is only called when the parser state is
+     * {@link Event#VALUE_NUMBER}.
+     *
+     * @return a long for a JSON number
+     * @throws IllegalStateException when the parser state is not
+     *      {@code VALUE_NUMBER}
+     * @see java.math.BigDecimal#longValue()
+     */
+    long getLong();
+
+    /**
+     * Returns a JSON number as a {@code BigDecimal}. The {@code BigDecimal}
+     * is created using {@code new BigDecimal(getString())}. This
+     * method should only called when the parser state is
+     * {@link Event#VALUE_NUMBER}.
+     *
+     * @return a {@code BigDecimal} for a JSON number
+     * @throws IllegalStateException when the parser state is not
+     *      {@code VALUE_NUMBER}
+     */
+    BigDecimal getBigDecimal();
+
+    /**
+     * Return the location that corresponds to the parser's current state in
+     * the JSON input source. The location information is only valid in the
+     * current parser state (or until the parser is advanced to a next state).
+     *
+     * @return a non-null location corresponding to the current parser state
+     * in JSON input source
+     */
+    JsonLocation getLocation();
+
+    /**
+     * Returns a {@code JsonObject} and advances the parser to the
+     * corresponding {@code END_OBJECT}.
+     *
+     * @return the {@code JsonObject} at the current parser position
+     *
+     * @throws IllegalStateException when the parser state is not
+     *     {@code START_OBJECT}
+     *
+     * @since 1.1
+     */
+    default public JsonObject getObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a {@code JsonValue} at the current parser position.
+     * If the parser state is {@code START_ARRAY}, the behavior is
+     * the same as {@link #getArray}. If the parser state is
+     * {@code START_OBJECT}, the behavior is the same as
+     * {@link #getObject}. For all other cases, if applicable, the JSON value is
+     * read and returned.
+     *
+     * @return the {@code JsonValue} at the current parser position.
+     * @throws IllegalStateException when the parser state is
+     *     {@code END_OBJECT} or {@code END_ARRAY}
+     *
+     * @since 1.1
+     */
+    default public JsonValue getValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a {@code JsonArray} and advance the parser to the
+     * the corresponding {@code END_ARRAY}.
+     *
+     * @return the {@code JsonArray} at the current parser position
+     *
+     * @throws IllegalStateException when the parser state is not
+     *     {@code START_ARRAY}
+     *
+     * @since 1.1
+     */
+    default public JsonArray getArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a stream of the {@code JsonArray} elements.
+     * The parser state must be {@code START_ARRAY}.
+     * The elements are read lazily, on an as-needed basis, as
+     * required by the stream operations.
+     * If the stream operations do not consume
+     * all of the array elements, {@link skipArray} can be used to
+     * skip the unprocessed array elements.
+     *
+     * @return a stream of elements of the {@code JsonArray}
+     *
+     * @throws IllegalStateException when the parser state is not
+     *     {@code START_ARRAY}
+     *
+     * @since 1.1
+     */
+    default public Stream<JsonValue> getArrayStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a stream of the {@code JsonObject}'s
+     * name/value pairs. The parser state must be {@code START_OBJECT}.
+     * The name/value pairs are read lazily, on an as-needed basis, as
+     * required by the stream operations.
+     * If the stream operations do not consume
+     * all of the object's name/value pairs, {@link skipObject} can be
+     * used to skip the unprocessed elements.
+     *
+     * @return a stream of name/value pairs of the {@code JsonObject}
+     *
+     * @throws IllegalStateException when the parser state is not
+     *     {@code START_OBJECT}
+     *
+     * @since 1.1
+     */
+    default public Stream<Map.Entry<String,JsonValue>> getObjectStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a stream of {@code JsonValue} from a sequence of
+     * JSON values. The values are read lazily, on an as-needed basis,
+     * as needed by the stream operations.
+     *
+     * @return a Stream of {@code JsonValue}
+     *
+     * @throws IllegalStateException if the parser is in an array or object.
+     *
+     * @since 1.1
+     */
+    default public Stream<JsonValue> getValueStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Advance the parser to {@code END_ARRAY}.
+     * If the parser is in array context, i.e. it has previously
+     * encountered a {@code START_ARRAY} without encountering the
+     * corresponding {@code END_ARRAY}, the parser is advanced to
+     * the corresponding {@code END_ARRAY}.
+     * If the parser is not in any array context, nothing happens.
+     *
+     * @since 1.1
+     */
+    default public void skipArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Advance the parser to {@code END_OBJECT}.
+     * If the parser is in object context, i.e. it has previously
+     * encountered a {@code START_OBJECT} without encountering the
+     * corresponding {@code END_OBJECT}, the parser is advanced to
+     * the corresponding {@code END_OBJECT}.
+     * If the parser is not in any object context, nothing happens.
+     *
+     * @since 1.1
+     */
+    default public void skipObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Closes this parser and frees any resources associated with the
+     * parser. This method closes the underlying input source.
+     *
+     * @throws jakarta.json.JsonException if an i/o error occurs (IOException
+     * would be cause of JsonException)
+     */
+    @Override
+    void close();
+}
diff --git a/api/src/main/java/jakarta/json/stream/JsonParserFactory.java b/api/src/main/java/jakarta/json/stream/JsonParserFactory.java
new file mode 100644
index 0000000..3434411
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonParserFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * Factory for creating {@link JsonParser} instances. If a factory
+ * instance is configured with a configuration, the configuration applies
+ * to all parser instances created using that factory instance.
+ *
+ * <p>
+ * The class {@link jakarta.json.Json Json} also provides methods to create
+ * {@link JsonParser} instances, but using {@code JsonParserFactory} is 
+ * preferred when creating multiple parser instances as shown in the following
+ * example:
+ *
+ * <pre>
+ * <code>
+ * JsonParserFactory factory = Json.createParserFactory();
+ * JsonParser parser1 = factory.createParser(...);
+ * JsonParser parser2 = factory.createParser(...);
+ * </code>
+ * </pre>
+ *
+ * <p> All the methods in this class are safe for use by multiple concurrent
+ * threads.
+ */
+public interface JsonParserFactory {
+
+    /**
+     * Creates a JSON parser from a character stream.
+     *
+     * @param reader a i/o reader from which JSON is to be read
+     * @return the created JSON parser
+     */
+    JsonParser createParser(Reader reader);
+
+    /**
+     * Creates a JSON parser from the specified byte stream.
+     * The character encoding of the stream is determined
+     * as specified in <a href="http://tools.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
+     *
+     * @param in i/o stream from which JSON is to be read
+     * @return the created JSON parser
+     * @throws jakarta.json.JsonException if encoding cannot be determined
+     *         or i/o error (IOException would be cause of JsonException)
+     */
+    JsonParser createParser(InputStream in);
+
+    /**
+     * Creates a JSON parser from the specified byte stream.
+     * The bytes of the stream are decoded to characters using the
+     * specified charset.
+     *
+     * @param in i/o stream from which JSON is to be read
+     * @param charset a charset
+     * @return the created JSON parser
+     */
+    JsonParser createParser(InputStream in, Charset charset);
+
+    /**
+     * Creates a JSON parser from the specified JSON object.
+     *
+     * @param obj a JSON object
+     * @return the created JSON parser
+     */
+    JsonParser createParser(JsonObject obj);
+
+    /**
+     * Creates a JSON parser from the specified JSON array.
+     *
+     * @param array a JSON array
+     * @return the created JSON parser
+     */
+    JsonParser createParser(JsonArray array);
+
+    /**
+     * Returns a read-only map of supported provider specific configuration
+     * properties that are used to configure the JSON parsers.
+     * If there are any specified configuration properties that are not
+     * supported by the provider, they won't be part of the returned map.
+     *
+     * @return a map of supported provider specific properties that are used
+     * to configure the created parsers. The map may be empty but not null
+     */
+    Map<String, ?> getConfigInUse();
+
+}
diff --git a/api/src/main/java/jakarta/json/stream/JsonParsingException.java b/api/src/main/java/jakarta/json/stream/JsonParsingException.java
new file mode 100644
index 0000000..ffca1e0
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/JsonParsingException.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.json.stream;
+
+import jakarta.json.JsonException;
+
+/**
+ * {@code JsonParsingException} is used when an incorrect JSON is
+ * being parsed.
+ */
+public class JsonParsingException extends JsonException {
+
+    private final JsonLocation location;
+
+    /**
+     * Constructs a new runtime exception with the specified detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     * @param location the location of the incorrect JSON
+     */
+    public JsonParsingException(String message, JsonLocation location) {
+        super(message);
+        this.location = location;
+    }
+
+    /**
+     * Constructs a new runtime exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this runtime exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method). (A <code>null</code> value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     * @param location the location of the incorrect JSON
+     */
+    public JsonParsingException(String message, Throwable cause, JsonLocation location) {
+        super(message, cause);
+        this.location = location;
+    }
+
+    /**
+     * Return the location of the incorrect JSON.
+     *
+     * @return the non-null location of the incorrect JSON
+     */
+    public JsonLocation getLocation() {
+        return location;
+    }
+
+}
+
diff --git a/api/src/main/java/jakarta/json/stream/package-info.java b/api/src/main/java/jakarta/json/stream/package-info.java
new file mode 100644
index 0000000..bd57cdb
--- /dev/null
+++ b/api/src/main/java/jakarta/json/stream/package-info.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Provides a streaming API to parse and generate
+ * <a href="http://json.org/">JSON</a>.
+ *
+ * <p>
+ * The streaming API consists of the interfaces
+ * {@link jakarta.json.stream.JsonParser} and
+ * {@link jakarta.json.stream.JsonGenerator}. The interface {@code JsonParser}
+ * contains methods to parse JSON in a streaming way. The interface
+ * {@code JsonGenerator} contains methods to write JSON to an output source
+ * in a streaming way.
+ *
+ * <p>
+ * {@code JsonParser} provides forward, read-only access to JSON data using the
+ * pull parsing programming model. In this model the application code controls
+ * the thread and calls methods in the parser interface to move the parser
+ * forward or to obtain JSON data from the current state of the parser.
+ *
+ * <p>
+ * {@code JsonGenerator} provides methods to write JSON to an output source.
+ * The generator writes name/value pairs in JSON objects and values in JSON
+ * arrays.
+ * 
+ * <p>
+ * The streaming API is a low-level API designed to process large amounts of
+ * JSON data efficiently. Other JSON frameworks (such as JSON binding) can be
+ * implemented using this API.
+ *
+ * @since JSON Processing 1.0
+ */
+package jakarta.json.stream;
diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java
new file mode 100644
index 0000000..46a321c
--- /dev/null
+++ b/api/src/main/java/module-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jakarta JSON Processing API.
+ */
+module jakarta.json {
+    exports jakarta.json;
+    exports jakarta.json.spi;
+    exports jakarta.json.stream;
+    uses jakarta.json.spi.JsonProvider;
+}
diff --git a/api/src/main/javadoc/doc-files/speclicense.html b/api/src/main/javadoc/doc-files/speclicense.html
new file mode 100644
index 0000000..ba29e5e
--- /dev/null
+++ b/api/src/main/javadoc/doc-files/speclicense.html
@@ -0,0 +1,72 @@
+<html>
+<head>
+<title>Eclipse Foundation Specification License - v1.0</title>
+</head>
+<body>
+<h1>Eclipse Foundation Specification License - v1.0</h1>
+<p>By using and/or copying this document, or the Eclipse Foundation
+  document from which this statement is linked, you (the licensee) agree
+  that you have read, understood, and will comply with the following
+  terms and conditions:</p>
+
+<p>Permission to copy, and distribute the contents of this document, or
+  the Eclipse Foundation document from which this statement is linked, in
+  any medium for any purpose and without fee or royalty is hereby
+  granted, provided that you include the following on ALL copies of the
+  document, or portions thereof, that you use:</p>
+
+<ul>
+  <li> link or URL to the original Eclipse Foundation document.</li>
+  <li>All existing copyright notices, or if one does not exist, a notice
+      (hypertext is preferred, but a textual representation is permitted)
+      of the form: &quot;Copyright &copy; [$date-of-document]
+      &ldquo;Eclipse Foundation, Inc. &lt;&lt;url to this license&gt;&gt;
+      &quot;
+  </li>
+</ul>
+
+<p>Inclusion of the full text of this NOTICE must be provided. We
+  request that authorship attribution be provided in any software,
+  documents, or other items or products that you create pursuant to the
+  implementation of the contents of this document, or any portion
+  thereof.</p>
+
+<p>No right to create modifications or derivatives of Eclipse Foundation
+  documents is granted pursuant to this license, except anyone may
+  prepare and distribute derivative works and portions of this document
+  in software that implements the specification, in supporting materials
+  accompanying such software, and in documentation of such software,
+  PROVIDED that all such works include the notice below. HOWEVER, the
+  publication of derivative works of this document for use as a technical
+  specification is expressly prohibited.</p>
+
+<p>The notice is:</p>
+
+<p>&quot;Copyright &copy; 2018 Eclipse Foundation. This software or
+  document includes material copied from or derived from [title and URI
+  of the Eclipse Foundation specification document].&quot;</p>
+
+<h2>Disclaimers</h2>
+
+<p>THIS DOCUMENT IS PROVIDED &quot;AS IS,&quot; AND THE COPYRIGHT
+  HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR
+  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+  NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE
+  SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS
+  WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR
+  OTHER RIGHTS.</p>
+
+<p>THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE
+  FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT
+  OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE
+  CONTENTS THEREOF.</p>
+
+<p>The name and trademarks of the copyright holders or the Eclipse
+  Foundation may NOT be used in advertising or publicity pertaining to
+  this document or its contents without specific, written prior
+  permission. Title to copyright in this document will at all times
+  remain with copyright holders.</p>
+
+</body>
+</html>
diff --git a/api/src/main/javadoc/overview.html b/api/src/main/javadoc/overview.html
new file mode 100644
index 0000000..2b82208
--- /dev/null
+++ b/api/src/main/javadoc/overview.html
@@ -0,0 +1,108 @@
+<html>
+<!--
+
+    Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<body>
+Jakarta JSON Processing provides portable APIs to parse,
+generate, transform, and query <a href="http://json.org/">JSON</a> using the
+streaming API or the object model API.
+
+<p>The Streaming API provides a way to parsing and generation of JSON in a
+streaming fashion. It hands over parsing and generation control to the
+programmer. The streaming API provides an event-based parser and allows an
+application developer to ask for the next event rather than handling the event
+in a callback. This gives a developer more procedural control over
+the processing of the JSON. Application code can process or discard
+the parser event, and ask for the next event(pull the event). The
+streaming model is adequate for local processing where random access of other
+parts of the data is not required. Similarly, the streaming API provides
+a way to generate well-formed JSON to a stream by writing one event at a time.
+
+<p>The object model API creates a random access tree-like structure that
+represents the JSON data in memory. The tree can then be navigated and
+queried. This programming model is the most flexible and enables processing
+that requires random access to the complete contents of the tree. However,
+it is often not as efficient as the streaming model and requires more memory.
+The object model generates JSON output by navigating the entire tree at once.
+
+<h1>The Streaming API</h1>
+<p>The streaming API is similar to the StAX API for XML and consists of the
+interfaces {@link jakarta.json.stream.JsonParser} and
+{@link jakarta.json.stream.JsonGenerator}. {@code JsonParser}
+contains methods to parse JSON data using the streaming model.
+{@code JsonGenerator} contains methods to write JSON data to an ouptut source.
+
+<p>{@code JsonParser} provides forward, read-only access to
+JSON data using the pull parsing programming model. In this model the
+application code controls the thread and calls methods in the parser interface
+to move the parser forward or to obtain JSON data from the current state of
+the parser. Refer to
+<a href="jakarta.json/jakarta/json/stream/JsonParser.html#JsonParserExample2">this example</a>
+for more details.
+
+<p>{@code JsonGenerator} provides methods to write JSON to a stream. The
+generator writes name/value pairs in JSON objects and values in JSON arrays.
+Refer to
+<a href="jakarta.json/jakarta/json/stream/JsonGenerator.html#JsonGeneratorExample3">this
+example</a> for more details.
+
+<p>The streaming API is a low-level API designed to process large amounts of
+JSON data efficiently. Other JSON frameworks (such as JSON binding) can be
+implemented using this API.</p>
+
+<h1>The Object Model API</h1>
+<p>The object model API is similar to the DOM API for XML. It is a high-level
+API that provides immutable object models for JSON object and array structures.
+These JSON structures are represented as object models using the Java types
+{@link jakarta.json.JsonObject} and {@link jakarta.json.JsonArray}.
+{@code JsonObject} provides a {@link java.util.Map} view to access the unordered
+collection of zero or more name/value pairs from the model. Similarly,
+{@code JsonArray} provides a {@link java.util.List} view to access the ordered
+sequence of zero or more values from the model.
+
+<p>The object model API uses builder patterns to create these object models.
+Application code can use the interface {@link jakarta.json.JsonObjectBuilder}
+to create models that represent JSON objects. The resulting model is of type
+{@code JsonObject}. Refer to
+<a href="jakarta.json/jakarta/json/JsonObjectBuilder.html#JsonObjectBuilderExample1">this example</a>
+for more details. Application code can use the interface
+{@link jakarta.json.JsonArrayBuilder} to create models that represent JSON arrays.
+The resulting model is of type {@code JsonArray}. Refer to
+<a href="jakarta.json/jakarta/json/JsonArrayBuilder.html#JsonArrayBuilderExample1">this example</a>
+for more details.
+
+<p>These object models can also be created from an input source (such as
+{@link java.io.InputStream} or {@link java.io.Reader}) using the interface
+{@link jakarta.json.JsonReader}.
+<a href="jakarta.json/jakarta/json/JsonReader.html#JsonReaderExample1">This example</a> shows
+how to read and create an empty {@code JsonArray} model using the interface
+{@code JsonReader}. Similarly, these object models can be written to an output
+source (such as {@link java.io.OutputStream} or {@link java.io.Writer}) using
+the class {@link jakarta.json.JsonWriter}.
+<a href="jakarta.json/jakarta/json/JsonWriter.html#JsonWriterExample1">This example</a> shows
+how to write an empty {@code JsonObject} model using the interface
+{@code JsonWriter}.
+
+<h1>JSON Pointer, JSON Patch, and JSON Merge Patch</h1>
+Jakarta JSON Processing supports the latest standard on
+<a href="http://tools.ietf.org/html/rfc6901">JSON Pointer</a>,
+<a Href="http://tools.ietf.org/html/rfc6902">JSON Patch</a>, and
+<a Href="http://tools.ietf.org/html/rfc7396">JSON Merge Patch</a>.
+
+</body>
+</html>
diff --git a/bundles/pom.xml b/bundles/pom.xml
new file mode 100644
index 0000000..f90a6fb
--- /dev/null
+++ b/bundles/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>json-bundles</artifactId>
+    <packaging>pom</packaging>
+    <name>Jakarta JSON Processing bundles</name>
+    <description>Jakarta JSON Processing bundles</description>
+    <url>https://github.com/eclipse-ee4j/jsonp</url>
+
+    <modules>
+        <module>ri</module>
+    </modules>
+</project>
diff --git a/bundles/ri/pom.xml b/bundles/ri/pom.xml
new file mode 100755
index 0000000..cfc8cd0
--- /dev/null
+++ b/bundles/ri/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json-bundles</artifactId>
+        <version>2.0.1</version>
+    </parent>
+
+    <artifactId>json-ri-bundle</artifactId>
+    <name>RI Distribution zip bundle</name>
+    <packaging>pom</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+            <classifier>module</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jsonp-jaxrs</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>jakarta.json-ri-${impl_version}</finalName>
+                            <descriptors>
+                                <descriptor>src/main/assembly/archive.xml</descriptor>
+                            </descriptors>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/bundles/ri/src/main/assembly/archive.xml b/bundles/ri/src/main/assembly/archive.xml
new file mode 100755
index 0000000..1c9e053
--- /dev/null
+++ b/bundles/ri/src/main/assembly/archive.xml
@@ -0,0 +1,62 @@
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<assembly>
+    <id>dist</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <files>
+        <file>
+            <source>src/main/resources/README.txt</source>
+            <outputDirectory></outputDirectory>
+            <filtered>true</filtered>
+        </file>
+        <file>
+            <source>src/main/resources/LICENSE.md</source>
+            <outputDirectory></outputDirectory>
+        </file>
+    </files>
+    <dependencySets>
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>mods</outputDirectory>
+            <includes>
+                <include>jakarta.json:jakarta.json-api:*</include>
+                <include>org.glassfish:jakarta.json:jar:module:*</include>
+            </includes>
+        </dependencySet>
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>standalone</outputDirectory>
+            <includes>
+                <include>org.glassfish:jakarta.json</include>
+            </includes>
+            <excludes>
+                <exclude>org.glassfish:jakarta.json:jar:module:*</exclude>
+            </excludes>
+        </dependencySet>
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>jaxrs</outputDirectory>
+            <includes>
+                <include>org.glassfish:jsonp-jaxrs*</include>
+            </includes>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/bundles/ri/src/main/resources/LICENSE.md b/bundles/ri/src/main/resources/LICENSE.md
new file mode 100644
index 0000000..5de3d1b
--- /dev/null
+++ b/bundles/ri/src/main/resources/LICENSE.md
@@ -0,0 +1,637 @@
+# Eclipse Public License - v 2.0
+
+        THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+        PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+        OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+    1. DEFINITIONS
+
+    "Contribution" means:
+
+      a) in the case of the initial Contributor, the initial content
+         Distributed under this Agreement, and
+
+      b) in the case of each subsequent Contributor: 
+         i) changes to the Program, and 
+         ii) additions to the Program;
+      where such changes and/or additions to the Program originate from
+      and are Distributed by that particular Contributor. A Contribution
+      "originates" from a Contributor if it was added to the Program by
+      such Contributor itself or anyone acting on such Contributor's behalf.
+      Contributions do not include changes or additions to the Program that
+      are not Modified Works.
+
+    "Contributor" means any person or entity that Distributes the Program.
+
+    "Licensed Patents" mean patent claims licensable by a Contributor which
+    are necessarily infringed by the use or sale of its Contribution alone
+    or when combined with the Program.
+
+    "Program" means the Contributions Distributed in accordance with this
+    Agreement.
+
+    "Recipient" means anyone who receives the Program under this Agreement
+    or any Secondary License (as applicable), including Contributors.
+
+    "Derivative Works" shall mean any work, whether in Source Code or other
+    form, that is based on (or derived from) the Program and for which the
+    editorial revisions, annotations, elaborations, or other modifications
+    represent, as a whole, an original work of authorship.
+
+    "Modified Works" shall mean any work in Source Code or other form that
+    results from an addition to, deletion from, or modification of the
+    contents of the Program, including, for purposes of clarity any new file
+    in Source Code form that contains any contents of the Program. Modified
+    Works shall not include works that contain only declarations,
+    interfaces, types, classes, structures, or files of the Program solely
+    in each case in order to link to, bind by name, or subclass the Program
+    or Modified Works thereof.
+
+    "Distribute" means the acts of a) distributing or b) making available
+    in any manner that enables the transfer of a copy.
+
+    "Source Code" means the form of a Program preferred for making
+    modifications, including but not limited to software source code,
+    documentation source, and configuration files.
+
+    "Secondary License" means either the GNU General Public License,
+    Version 2.0, or any later versions of that license, including any
+    exceptions or additional permissions as identified by the initial
+    Contributor.
+
+    2. GRANT OF RIGHTS
+
+      a) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free copyright
+      license to reproduce, prepare Derivative Works of, publicly display,
+      publicly perform, Distribute and sublicense the Contribution of such
+      Contributor, if any, and such Derivative Works.
+
+      b) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free patent
+      license under Licensed Patents to make, use, sell, offer to sell,
+      import and otherwise transfer the Contribution of such Contributor,
+      if any, in Source Code or other form. This patent license shall
+      apply to the combination of the Contribution and the Program if, at
+      the time the Contribution is added by the Contributor, such addition
+      of the Contribution causes such combination to be covered by the
+      Licensed Patents. The patent license shall not apply to any other
+      combinations which include the Contribution. No hardware per se is
+      licensed hereunder.
+
+      c) Recipient understands that although each Contributor grants the
+      licenses to its Contributions set forth herein, no assurances are
+      provided by any Contributor that the Program does not infringe the
+      patent or other intellectual property rights of any other entity.
+      Each Contributor disclaims any liability to Recipient for claims
+      brought by any other entity based on infringement of intellectual
+      property rights or otherwise. As a condition to exercising the
+      rights and licenses granted hereunder, each Recipient hereby
+      assumes sole responsibility to secure any other intellectual
+      property rights needed, if any. For example, if a third party
+      patent license is required to allow Recipient to Distribute the
+      Program, it is Recipient's responsibility to acquire that license
+      before distributing the Program.
+
+      d) Each Contributor represents that to its knowledge it has
+      sufficient copyright rights in its Contribution, if any, to grant
+      the copyright license set forth in this Agreement.
+
+      e) Notwithstanding the terms of any Secondary License, no
+      Contributor makes additional grants to any Recipient (other than
+      those set forth in this Agreement) as a result of such Recipient's
+      receipt of the Program under the terms of a Secondary License
+      (if permitted under the terms of Section 3).
+
+    3. REQUIREMENTS
+
+    3.1 If a Contributor Distributes the Program in any form, then:
+
+      a) the Program must also be made available as Source Code, in
+      accordance with section 3.2, and the Contributor must accompany
+      the Program with a statement that the Source Code for the Program
+      is available under this Agreement, and informs Recipients how to
+      obtain it in a reasonable manner on or through a medium customarily
+      used for software exchange; and
+
+      b) the Contributor may Distribute the Program under a license
+      different than this Agreement, provided that such license:
+         i) effectively disclaims on behalf of all other Contributors all
+         warranties and conditions, express and implied, including
+         warranties or conditions of title and non-infringement, and
+         implied warranties or conditions of merchantability and fitness
+         for a particular purpose;
+
+         ii) effectively excludes on behalf of all other Contributors all
+         liability for damages, including direct, indirect, special,
+         incidental and consequential damages, such as lost profits;
+
+         iii) does not attempt to limit or alter the recipients' rights
+         in the Source Code under section 3.2; and
+
+         iv) requires any subsequent distribution of the Program by any
+         party to be under a license that satisfies the requirements
+         of this section 3.
+
+    3.2 When the Program is Distributed as Source Code:
+
+      a) it must be made available under this Agreement, or if the
+      Program (i) is combined with other material in a separate file or
+      files made available under a Secondary License, and (ii) the initial
+      Contributor attached to the Source Code the notice described in
+      Exhibit A of this Agreement, then the Program may be made available
+      under the terms of such Secondary Licenses, and
+
+      b) a copy of this Agreement must be included with each copy of
+      the Program.
+
+    3.3 Contributors may not remove or alter any copyright, patent,
+    trademark, attribution notices, disclaimers of warranty, or limitations
+    of liability ("notices") contained within the Program from any copy of
+    the Program which they Distribute, provided that Contributors may add
+    their own appropriate notices.
+
+    4. COMMERCIAL DISTRIBUTION
+
+    Commercial distributors of software may accept certain responsibilities
+    with respect to end users, business partners and the like. While this
+    license is intended to facilitate the commercial use of the Program,
+    the Contributor who includes the Program in a commercial product
+    offering should do so in a manner which does not create potential
+    liability for other Contributors. Therefore, if a Contributor includes
+    the Program in a commercial product offering, such Contributor
+    ("Commercial Contributor") hereby agrees to defend and indemnify every
+    other Contributor ("Indemnified Contributor") against any losses,
+    damages and costs (collectively "Losses") arising from claims, lawsuits
+    and other legal actions brought by a third party against the Indemnified
+    Contributor to the extent caused by the acts or omissions of such
+    Commercial Contributor in connection with its distribution of the Program
+    in a commercial product offering. The obligations in this section do not
+    apply to any claims or Losses relating to any actual or alleged
+    intellectual property infringement. In order to qualify, an Indemnified
+    Contributor must: a) promptly notify the Commercial Contributor in
+    writing of such claim, and b) allow the Commercial Contributor to control,
+    and cooperate with the Commercial Contributor in, the defense and any
+    related settlement negotiations. The Indemnified Contributor may
+    participate in any such claim at its own expense.
+
+    For example, a Contributor might include the Program in a commercial
+    product offering, Product X. That Contributor is then a Commercial
+    Contributor. If that Commercial Contributor then makes performance
+    claims, or offers warranties related to Product X, those performance
+    claims and warranties are such Commercial Contributor's responsibility
+    alone. Under this section, the Commercial Contributor would have to
+    defend claims against the other Contributors related to those performance
+    claims and warranties, and if a court requires any other Contributor to
+    pay any damages as a result, the Commercial Contributor must pay
+    those damages.
+
+    5. NO WARRANTY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+    BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+    IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+    TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+    PURPOSE. Each Recipient is solely responsible for determining the
+    appropriateness of using and distributing the Program and assumes all
+    risks associated with its exercise of rights under this Agreement,
+    including but not limited to the risks and costs of program errors,
+    compliance with applicable laws, damage to or loss of data, programs
+    or equipment, and unavailability or interruption of operations.
+
+    6. DISCLAIMER OF LIABILITY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+    SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+    PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+    EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGES.
+
+    7. GENERAL
+
+    If any provision of this Agreement is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this Agreement, and without further
+    action by the parties hereto, such provision shall be reformed to the
+    minimum extent necessary to make such provision valid and enforceable.
+
+    If Recipient institutes patent litigation against any entity
+    (including a cross-claim or counterclaim in a lawsuit) alleging that the
+    Program itself (excluding combinations of the Program with other software
+    or hardware) infringes such Recipient's patent(s), then such Recipient's
+    rights granted under Section 2(b) shall terminate as of the date such
+    litigation is filed.
+
+    All Recipient's rights under this Agreement shall terminate if it
+    fails to comply with any of the material terms or conditions of this
+    Agreement and does not cure such failure in a reasonable period of
+    time after becoming aware of such noncompliance. If all Recipient's
+    rights under this Agreement terminate, Recipient agrees to cease use
+    and distribution of the Program as soon as reasonably practicable.
+    However, Recipient's obligations under this Agreement and any licenses
+    granted by Recipient relating to the Program shall continue and survive.
+
+    Everyone is permitted to copy and distribute copies of this Agreement,
+    but in order to avoid inconsistency the Agreement is copyrighted and
+    may only be modified in the following manner. The Agreement Steward
+    reserves the right to publish new versions (including revisions) of
+    this Agreement from time to time. No one other than the Agreement
+    Steward has the right to modify this Agreement. The Eclipse Foundation
+    is the initial Agreement Steward. The Eclipse Foundation may assign the
+    responsibility to serve as the Agreement Steward to a suitable separate
+    entity. Each new version of the Agreement will be given a distinguishing
+    version number. The Program (including Contributions) may always be
+    Distributed subject to the version of the Agreement under which it was
+    received. In addition, after a new version of the Agreement is published,
+    Contributor may elect to Distribute the Program (including its
+    Contributions) under the new version.
+
+    Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+    receives no rights or licenses to the intellectual property of any
+    Contributor under this Agreement, whether expressly, by implication,
+    estoppel or otherwise. All rights in the Program not expressly granted
+    under this Agreement are reserved. Nothing in this Agreement is intended
+    to be enforceable by any entity that is not a Contributor or Recipient.
+    No third-party beneficiary rights are created under this Agreement.
+
+    Exhibit A - Form of Secondary Licenses Notice
+
+    "This Source Code may also be made available under the following 
+    Secondary Licenses when the conditions for such availability set forth 
+    in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+    version(s), and exceptions or additional permissions here}."
+
+      Simply including a copy of this Agreement, including this Exhibit A
+      is not sufficient to license the Source Code under Secondary Licenses.
+
+      If it is not possible or desirable to put the notice in a particular
+      file, then You may include the notice in a location (such as a LICENSE
+      file in a relevant directory) where a recipient would be likely to
+      look for such a notice.
+
+      You may add additional accurate notices of copyright ownership.
+
+---
+
+##    The GNU General Public License (GPL) Version 2, June 1991
+
+    Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+    51 Franklin Street, Fifth Floor
+    Boston, MA 02110-1335
+    USA
+
+    Everyone is permitted to copy and distribute verbatim copies
+    of this license document, but changing it is not allowed.
+
+    Preamble
+
+    The licenses for most software are designed to take away your freedom to
+    share and change it. By contrast, the GNU General Public License is
+    intended to guarantee your freedom to share and change free software--to
+    make sure the software is free for all its users. This General Public
+    License applies to most of the Free Software Foundation's software and
+    to any other program whose authors commit to using it. (Some other Free
+    Software Foundation software is covered by the GNU Library General
+    Public License instead.) You can apply it to your programs, too.
+
+    When we speak of free software, we are referring to freedom, not price.
+    Our General Public Licenses are designed to make sure that you have the
+    freedom to distribute copies of free software (and charge for this
+    service if you wish), that you receive source code or can get it if you
+    want it, that you can change the software or use pieces of it in new
+    free programs; and that you know you can do these things.
+
+    To protect your rights, we need to make restrictions that forbid anyone
+    to deny you these rights or to ask you to surrender the rights. These
+    restrictions translate to certain responsibilities for you if you
+    distribute copies of the software, or if you modify it.
+
+    For example, if you distribute copies of such a program, whether gratis
+    or for a fee, you must give the recipients all the rights that you have.
+    You must make sure that they, too, receive or can get the source code.
+    And you must show them these terms so they know their rights.
+
+    We protect your rights with two steps: (1) copyright the software, and
+    (2) offer you this license which gives you legal permission to copy,
+    distribute and/or modify the software.
+
+    Also, for each author's protection and ours, we want to make certain
+    that everyone understands that there is no warranty for this free
+    software. If the software is modified by someone else and passed on, we
+    want its recipients to know that what they have is not the original, so
+    that any problems introduced by others will not reflect on the original
+    authors' reputations.
+
+    Finally, any free program is threatened constantly by software patents.
+    We wish to avoid the danger that redistributors of a free program will
+    individually obtain patent licenses, in effect making the program
+    proprietary. To prevent this, we have made it clear that any patent must
+    be licensed for everyone's free use or not licensed at all.
+
+    The precise terms and conditions for copying, distribution and
+    modification follow.
+
+    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License applies to any program or other work which contains a
+    notice placed by the copyright holder saying it may be distributed under
+    the terms of this General Public License. The "Program", below, refers
+    to any such program or work, and a "work based on the Program" means
+    either the Program or any derivative work under copyright law: that is
+    to say, a work containing the Program or a portion of it, either
+    verbatim or with modifications and/or translated into another language.
+    (Hereinafter, translation is included without limitation in the term
+    "modification".) Each licensee is addressed as "you".
+
+    Activities other than copying, distribution and modification are not
+    covered by this License; they are outside its scope. The act of running
+    the Program is not restricted, and the output from the Program is
+    covered only if its contents constitute a work based on the Program
+    (independent of having been made by running the Program). Whether that
+    is true depends on what the Program does.
+
+    1. You may copy and distribute verbatim copies of the Program's source
+    code as you receive it, in any medium, provided that you conspicuously
+    and appropriately publish on each copy an appropriate copyright notice
+    and disclaimer of warranty; keep intact all the notices that refer to
+    this License and to the absence of any warranty; and give any other
+    recipients of the Program a copy of this License along with the Program.
+
+    You may charge a fee for the physical act of transferring a copy, and
+    you may at your option offer warranty protection in exchange for a fee.
+
+    2. You may modify your copy or copies of the Program or any portion of
+    it, thus forming a work based on the Program, and copy and distribute
+    such modifications or work under the terms of Section 1 above, provided
+    that you also meet all of these conditions:
+
+        a) You must cause the modified files to carry prominent notices
+        stating that you changed the files and the date of any change.
+
+        b) You must cause any work that you distribute or publish, that in
+        whole or in part contains or is derived from the Program or any part
+        thereof, to be licensed as a whole at no charge to all third parties
+        under the terms of this License.
+
+        c) If the modified program normally reads commands interactively
+        when run, you must cause it, when started running for such
+        interactive use in the most ordinary way, to print or display an
+        announcement including an appropriate copyright notice and a notice
+        that there is no warranty (or else, saying that you provide a
+        warranty) and that users may redistribute the program under these
+        conditions, and telling the user how to view a copy of this License.
+        (Exception: if the Program itself is interactive but does not
+        normally print such an announcement, your work based on the Program
+        is not required to print an announcement.)
+
+    These requirements apply to the modified work as a whole. If
+    identifiable sections of that work are not derived from the Program, and
+    can be reasonably considered independent and separate works in
+    themselves, then this License, and its terms, do not apply to those
+    sections when you distribute them as separate works. But when you
+    distribute the same sections as part of a whole which is a work based on
+    the Program, the distribution of the whole must be on the terms of this
+    License, whose permissions for other licensees extend to the entire
+    whole, and thus to each and every part regardless of who wrote it.
+
+    Thus, it is not the intent of this section to claim rights or contest
+    your rights to work written entirely by you; rather, the intent is to
+    exercise the right to control the distribution of derivative or
+    collective works based on the Program.
+
+    In addition, mere aggregation of another work not based on the Program
+    with the Program (or with a work based on the Program) on a volume of a
+    storage or distribution medium does not bring the other work under the
+    scope of this License.
+
+    3. You may copy and distribute the Program (or a work based on it,
+    under Section 2) in object code or executable form under the terms of
+    Sections 1 and 2 above provided that you also do one of the following:
+
+        a) Accompany it with the complete corresponding machine-readable
+        source code, which must be distributed under the terms of Sections 1
+        and 2 above on a medium customarily used for software interchange; or,
+
+        b) Accompany it with a written offer, valid for at least three
+        years, to give any third party, for a charge no more than your cost
+        of physically performing source distribution, a complete
+        machine-readable copy of the corresponding source code, to be
+        distributed under the terms of Sections 1 and 2 above on a medium
+        customarily used for software interchange; or,
+
+        c) Accompany it with the information you received as to the offer to
+        distribute corresponding source code. (This alternative is allowed
+        only for noncommercial distribution and only if you received the
+        program in object code or executable form with such an offer, in
+        accord with Subsection b above.)
+
+    The source code for a work means the preferred form of the work for
+    making modifications to it. For an executable work, complete source code
+    means all the source code for all modules it contains, plus any
+    associated interface definition files, plus the scripts used to control
+    compilation and installation of the executable. However, as a special
+    exception, the source code distributed need not include anything that is
+    normally distributed (in either source or binary form) with the major
+    components (compiler, kernel, and so on) of the operating system on
+    which the executable runs, unless that component itself accompanies the
+    executable.
+
+    If distribution of executable or object code is made by offering access
+    to copy from a designated place, then offering equivalent access to copy
+    the source code from the same place counts as distribution of the source
+    code, even though third parties are not compelled to copy the source
+    along with the object code.
+
+    4. You may not copy, modify, sublicense, or distribute the Program
+    except as expressly provided under this License. Any attempt otherwise
+    to copy, modify, sublicense or distribute the Program is void, and will
+    automatically terminate your rights under this License. However, parties
+    who have received copies, or rights, from you under this License will
+    not have their licenses terminated so long as such parties remain in
+    full compliance.
+
+    5. You are not required to accept this License, since you have not
+    signed it. However, nothing else grants you permission to modify or
+    distribute the Program or its derivative works. These actions are
+    prohibited by law if you do not accept this License. Therefore, by
+    modifying or distributing the Program (or any work based on the
+    Program), you indicate your acceptance of this License to do so, and all
+    its terms and conditions for copying, distributing or modifying the
+    Program or works based on it.
+
+    6. Each time you redistribute the Program (or any work based on the
+    Program), the recipient automatically receives a license from the
+    original licensor to copy, distribute or modify the Program subject to
+    these terms and conditions. You may not impose any further restrictions
+    on the recipients' exercise of the rights granted herein. You are not
+    responsible for enforcing compliance by third parties to this License.
+
+    7. If, as a consequence of a court judgment or allegation of patent
+    infringement or for any other reason (not limited to patent issues),
+    conditions are imposed on you (whether by court order, agreement or
+    otherwise) that contradict the conditions of this License, they do not
+    excuse you from the conditions of this License. If you cannot distribute
+    so as to satisfy simultaneously your obligations under this License and
+    any other pertinent obligations, then as a consequence you may not
+    distribute the Program at all. For example, if a patent license would
+    not permit royalty-free redistribution of the Program by all those who
+    receive copies directly or indirectly through you, then the only way you
+    could satisfy both it and this License would be to refrain entirely from
+    distribution of the Program.
+
+    If any portion of this section is held invalid or unenforceable under
+    any particular circumstance, the balance of the section is intended to
+    apply and the section as a whole is intended to apply in other
+    circumstances.
+
+    It is not the purpose of this section to induce you to infringe any
+    patents or other property right claims or to contest validity of any
+    such claims; this section has the sole purpose of protecting the
+    integrity of the free software distribution system, which is implemented
+    by public license practices. Many people have made generous
+    contributions to the wide range of software distributed through that
+    system in reliance on consistent application of that system; it is up to
+    the author/donor to decide if he or she is willing to distribute
+    software through any other system and a licensee cannot impose that choice.
+
+    This section is intended to make thoroughly clear what is believed to be
+    a consequence of the rest of this License.
+
+    8. If the distribution and/or use of the Program is restricted in
+    certain countries either by patents or by copyrighted interfaces, the
+    original copyright holder who places the Program under this License may
+    add an explicit geographical distribution limitation excluding those
+    countries, so that distribution is permitted only in or among countries
+    not thus excluded. In such case, this License incorporates the
+    limitation as if written in the body of this License.
+
+    9. The Free Software Foundation may publish revised and/or new
+    versions of the General Public License from time to time. Such new
+    versions will be similar in spirit to the present version, but may
+    differ in detail to address new problems or concerns.
+
+    Each version is given a distinguishing version number. If the Program
+    specifies a version number of this License which applies to it and "any
+    later version", you have the option of following the terms and
+    conditions either of that version or of any later version published by
+    the Free Software Foundation. If the Program does not specify a version
+    number of this License, you may choose any version ever published by the
+    Free Software Foundation.
+
+    10. If you wish to incorporate parts of the Program into other free
+    programs whose distribution conditions are different, write to the
+    author to ask for permission. For software which is copyrighted by the
+    Free Software Foundation, write to the Free Software Foundation; we
+    sometimes make exceptions for this. Our decision will be guided by the
+    two goals of preserving the free status of all derivatives of our free
+    software and of promoting the sharing and reuse of software generally.
+
+    NO WARRANTY
+
+    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+    WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+    EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+    OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+    EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+    YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+    NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+    AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+    END OF TERMS AND CONDITIONS
+
+    How to Apply These Terms to Your New Programs
+
+    If you develop a new program, and you want it to be of the greatest
+    possible use to the public, the best way to achieve this is to make it
+    free software which everyone can redistribute and change under these terms.
+
+    To do so, attach the following notices to the program. It is safest to
+    attach them to the start of each source file to most effectively convey
+    the exclusion of warranty; and each file should have at least the
+    "copyright" line and a pointer to where the full notice is found.
+
+        One line to give the program's name and a brief idea of what it does.
+        Copyright (C) <year> <name of author>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with this program; if not, write to the Free Software
+        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+    Also add information on how to contact you by electronic and paper mail.
+
+    If the program is interactive, make it output a short notice like this
+    when it starts in an interactive mode:
+
+        Gnomovision version 69, Copyright (C) year name of author
+        Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type
+        `show w'. This is free software, and you are welcome to redistribute
+        it under certain conditions; type `show c' for details.
+
+    The hypothetical commands `show w' and `show c' should show the
+    appropriate parts of the General Public License. Of course, the commands
+    you use may be called something other than `show w' and `show c'; they
+    could even be mouse-clicks or menu items--whatever suits your program.
+
+    You should also get your employer (if you work as a programmer) or your
+    school, if any, to sign a "copyright disclaimer" for the program, if
+    necessary. Here is a sample; alter the names:
+
+        Yoyodyne, Inc., hereby disclaims all copyright interest in the
+        program `Gnomovision' (which makes passes at compilers) written by
+        James Hacker.
+
+        signature of Ty Coon, 1 April 1989
+        Ty Coon, President of Vice
+
+    This General Public License does not permit incorporating your program
+    into proprietary programs. If your program is a subroutine library, you
+    may consider it more useful to permit linking proprietary applications
+    with the library. If this is what you want to do, use the GNU Library
+    General Public License instead of this License.
+
+---
+
+## CLASSPATH EXCEPTION
+
+    Linking this library statically or dynamically with other modules is
+    making a combined work based on this library.  Thus, the terms and
+    conditions of the GNU General Public License version 2 cover the whole
+    combination.
+
+    As a special exception, the copyright holders of this library give you
+    permission to link this library with independent modules to produce an
+    executable, regardless of the license terms of these independent
+    modules, and to copy and distribute the resulting executable under
+    terms of your choice, provided that you also meet, for each linked
+    independent module, the terms and conditions of the license of that
+    module.  An independent module is a module which is not derived from or
+    based on this library.  If you modify this library, you may extend this
+    exception to your version of the library, but you are not obligated to
+    do so.  If you do not wish to do so, delete this exception statement
+    from your version.
diff --git a/bundles/ri/src/main/resources/README.txt b/bundles/ri/src/main/resources/README.txt
new file mode 100644
index 0000000..7f7879f
--- /dev/null
+++ b/bundles/ri/src/main/resources/README.txt
@@ -0,0 +1,51 @@
+* standalone/jakarta.json-${project.version}.jar contains both Jakarta JSON Processing API
+  and its default provider implementation. Keep it in classpath for both compiling and running your application.
+  JPMS module name is: 'jakarta.json'
+
+For running on JPMS, following modules are provided:
+* mods/jakarta.json-api-${project.version}.jar - 'jakarta.json' module containing only API classes
+* mods/jakarta.json-${project.version}-module.jar - 'org.glassfish.jakarta.json' module containing implementation
+
+Integration with Jakarta RESTful Web Services:
+* jaxrs/jsonp-jaxrs-${project.version}.jar
+
+
+IMPORTANT NOTE: module names are not yet final and may change in the future releases
+
+
+* If you are running with maven, you can use the following maven coordinates:
+
+for standalone reference implementation which includes APIs and implementation classes:
+  <dependency>
+      <groupId>org.glassfish</groupId>
+      <artifactId>jakarta.json</artifactId>
+      <version>${project.version}</version>
+  </dependency>
+
+for APIs:
+  <dependency>
+      <groupId>jakarta.json</groupId>
+      <artifactId>jakarta.json-api</artifactId>
+      <version>${project.version}</version>
+  </dependency>
+
+for implementation only:
+  <dependency>
+      <groupId>org.glassfish</groupId>
+      <artifactId>jakarta.json</artifactId>
+      <classifier>module</classifier>
+      <version>${project.version}</version>
+  </dependency>
+
+for Jakarta RESTful Web Services integration module:
+  <dependency>
+      <groupId>org.glassfish</groupId>
+      <artifactId>jsonp-jaxrs</artifactId>
+      <version>${project.version}</version>
+  </dependency>
+
+
+* GlassFish 6.x already bundles latest Jakarta JSON Processing implementation and Jakarta RESTful Web Services integration module.
+If you deploy an application with GlassFish 6.x, your application (war/ear) doesn't have to bundle APIs nor the implementation jar.
+
+* Samples can be run from https://github.com/eclipse-ee4j/glassfish-samples
diff --git a/demos/LICENSE.md b/demos/LICENSE.md
new file mode 100644
index 0000000..c739f78
--- /dev/null
+++ b/demos/LICENSE.md
@@ -0,0 +1,29 @@
+
+    Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+   
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+   
+      - Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+   
+      - Redistributions in binary form must reproduce the above copyright
+        notice, this list of conditions and the following disclaimer in the
+        documentation and/or other materials provided with the distribution.
+   
+      - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+   
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/demos/NOTICE.md b/demos/NOTICE.md
new file mode 100644
index 0000000..b4cb5ba
--- /dev/null
+++ b/demos/NOTICE.md
@@ -0,0 +1,49 @@
+# Notices for Eclipse Project for JSON Processing
+
+This content is produced and maintained by the Eclipse Project for JSON
+Processing project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.jsonp
+
+## Trademarks
+
+Eclipse Project for JSON Processing is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License, version 2 with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jsonp
+
+## Third-party Content
+
+JUnit (4.12)
+
+* License: Eclipse Public License
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
diff --git a/demos/customprovider-jdk9/pom.xml b/demos/customprovider-jdk9/pom.xml
new file mode 100644
index 0000000..73fb09d
--- /dev/null
+++ b/demos/customprovider-jdk9/pom.xml
@@ -0,0 +1,88 @@
+<!--
+
+    Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <url>http://maven.apache.org</url>
+    <artifactId>customprovider-jdk9</artifactId>
+
+    <name>customprovider-jdk9</name>
+
+    <properties>
+        <mod.dir>${project.build.directory}/mods</mod.dir>
+    </properties>
+    <build>
+        <finalName>customprovider-jdk9</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>prepare-endorsed</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${mod.dir}</outputDirectory>
+                            <silent>false</silent>
+                            <!--<scope>compile</scope>-->
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <id>test</id>
+                    </execution>
+                </executions>
+                <configuration>
+                    <argLine>
+                        --module-path ${project.build.directory}/classes:${mod.dir}:${project.build.directory}/test-classes
+                        --add-modules org.glassfish.jakarta.json.demos.customprovider
+                    </argLine>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/demos/customprovider-jdk9/src/main/java/module-info.java b/demos/customprovider-jdk9/src/main/java/module-info.java
new file mode 100644
index 0000000..dfdd58a
--- /dev/null
+++ b/demos/customprovider-jdk9/src/main/java/module-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+module org.glassfish.jakarta.json.demos.customprovider {
+    requires transitive jakarta.json;
+    exports org.glassfish.json.customprovider;
+    provides jakarta.json.spi.JsonProvider with org.glassfish.json.customprovider.TestProvider;
+}
diff --git a/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java
new file mode 100644
index 0000000..ebe196f
--- /dev/null
+++ b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.json.customprovider;
+
+import jakarta.json.JsonException;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerator;
+import java.io.IOException;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class TestGenerator implements JsonGenerator {
+    private final Writer writer;
+
+    public TestGenerator(Writer writer) {
+        this.writer = writer;
+    }
+
+    @Override
+    public void flush() {
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String fieldValue) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        try {
+            writer.write("[");
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        return null;
+    }
+
+
+    @Override
+    public JsonGenerator write(int value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        try {
+            writer.write("]");
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+        return this;
+    }
+
+    @Override
+    public void close() {
+        try {
+            writer.close();
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+    }
+
+    @Override
+    public JsonGenerator writeKey(String string) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+}
diff --git a/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java
new file mode 100644
index 0000000..fe1fb76
--- /dev/null
+++ b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.json.customprovider;
+
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.JsonWriter;
+import jakarta.json.JsonWriterFactory;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class TestProvider extends JsonProvider {
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return new TestGenerator(writer);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return new TestGenerator(new OutputStreamWriter(out));
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return null;
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return null;
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return null;
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return null;
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return null;
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return null;
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return null;
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return null;
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(Map<String, ?> config) {
+        return null;
+    }
+
+
+}
diff --git a/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java b/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java
new file mode 100644
index 0000000..55c0a70
--- /dev/null
+++ b/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package customprovider.test;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonGenerator;
+import org.glassfish.json.customprovider.TestGenerator;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author lukas
+ */
+public class TestProviderTest {
+
+    @Test
+    public void hello() {
+        try (JsonGenerator generator = Json.createGenerator(System.out)) {
+            Assert.assertTrue("TestGenerator is not picked up", generator instanceof TestGenerator);
+            generator.writeStartArray().writeEnd();
+        }
+        System.out.println();
+        System.out.println("Hurray!!!");
+    }
+}
diff --git a/demos/facebook/pom.xml b/demos/facebook/pom.xml
new file mode 100644
index 0000000..8dc9048
--- /dev/null
+++ b/demos/facebook/pom.xml
@@ -0,0 +1,101 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>jar</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>jsondemos-facebook</artifactId>
+
+    <properties>
+        <main.class>org.glassfish.jsondemos.facebook.FacebookObjectSearch</main.class>
+        <!--
+        <main.class>org.glassfish.jsondemos.facebook.FacebookStreamSearch</main.class>
+        -->
+        <modules.directory>${project.build.directory}/modules</modules.directory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <configuration>
+                    <stripVersion>true</stripVersion>
+                    <outputDirectory>${modules.directory}</outputDirectory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>get-dependencies</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>get-project-artifact</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>${project.groupId}</groupId>
+                                    <artifactId>${project.artifactId}</artifactId>
+                                    <version>${project.version}</version>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <executable>java</executable>
+                    <arguments>
+                        <argument>--module-path</argument>
+                        <argument>${modules.directory}</argument>
+                        <argument>-m</argument>
+                        <argument>org.glassfish.jakarta.json.demos.facebook/${main.class}</argument>
+                    </arguments>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>example-facebook</id>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/demos/facebook/src/main/java/module-info.java b/demos/facebook/src/main/java/module-info.java
new file mode 100644
index 0000000..63f6774
--- /dev/null
+++ b/demos/facebook/src/main/java/module-info.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+module org.glassfish.jakarta.json.demos.facebook {
+    requires jakarta.json;
+}
diff --git a/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java
new file mode 100644
index 0000000..41114d1
--- /dev/null
+++ b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.facebook;
+
+import jakarta.json.*;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * Parses JSON from facebook graph API using object model API.
+ * JSON would like :
+ *
+ * {
+ *   data: [
+ *     { "from" : { "name" : "xxx", ... }, "message: "yyy", ... },
+ *     { "from" : { "name" : "ppp", ... }, "message: "qqq", ... },
+ *      ...
+ *   ],
+ *   ...
+ * }
+ *
+ * This codes writes the facebook posts to output as follows:
+ * xxx: yyy
+ * --------
+ * ppp: qqq
+ * --------
+ *
+ * @author Jitendra Kotamraju
+ */
+public class FacebookObjectSearch {
+
+    public static void main(String... args) throws Exception {
+        try (InputStream is = getSearchStream();
+             JsonReader rdr = Json.createReader(is)) {
+
+            JsonObject obj = rdr.readObject();
+            JsonArray results = obj.getJsonArray("data");
+            for (JsonObject result : results.getValuesAs(JsonObject.class)) {
+                JsonValue value = result.get("from");
+                if (value != null && value instanceof JsonObject) {
+                    System.out.print(((JsonObject)value).getString("name", "anon"));
+                }
+                System.out.print(": ");
+                System.out.println(result.getString("message", ""));
+                System.out.println("-----------");
+            }
+        }
+    }
+
+    static InputStream getSearchStream() throws Exception {
+        Properties config = new Properties();
+        config.load(FacebookObjectSearch.class.getResourceAsStream(
+                "/facebookconfig.properties"));
+        final String accessToken = (String)config.get("access_token");
+
+        // Gets the search stream
+        String searchUrl = "https://graph.facebook.com/search?q=tamil&type=post&access_token=";
+        URL url = new URL(searchUrl+accessToken);
+        HttpURLConnection con = (HttpURLConnection)url.openConnection();
+        return con.getInputStream();
+    }
+
+}
diff --git a/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java
new file mode 100644
index 0000000..be1aa88
--- /dev/null
+++ b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.facebook;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import java.io.*;
+
+/**
+ * Parses JSON from facebook graph API using streaming API.
+ * JSON would like :
+ *
+ * {
+ *   data: [
+ *     { "from" : { "name" : "xxx", ... }, "message: "yyy", ... },
+ *     { "from" : { "name" : "ppp", ... }, "message: "qqq", ... },
+ *      ...
+ *   ],
+ *   ...
+ * }
+ *
+ * This codes writes the facebook posts to output as follows:
+ * xxx: yyy
+ * --------
+ * ppp: qqq
+ * --------
+ *
+ * @author Jitendra Kotamraju
+ */
+public class FacebookStreamSearch {
+
+    public static void main(String... args) throws Exception {
+        try (InputStream is = FacebookObjectSearch.getSearchStream();
+             JsonParser parser = Json.createParser(is)) {
+            while (parser.hasNext()) {
+                Event e = parser.next();
+                if (e == Event.KEY_NAME) {
+                    switch (parser.getString()) {
+                        case "name":
+                            parser.next();
+                            System.out.print(parser.getString());
+                            System.out.print(": ");
+                            break;
+                        case "message":
+                            parser.next();
+                            System.out.println(parser.getString());
+                            System.out.println("---------");
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/demos/facebook/src/main/resources/facebookconfig.properties b/demos/facebook/src/main/resources/facebookconfig.properties
new file mode 100644
index 0000000..f801f7d
--- /dev/null
+++ b/demos/facebook/src/main/resources/facebookconfig.properties
@@ -0,0 +1,12 @@
+#
+# Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+
+access_token=
diff --git a/demos/jaxrs/pom.xml b/demos/jaxrs/pom.xml
new file mode 100644
index 0000000..d907939
--- /dev/null
+++ b/demos/jaxrs/pom.xml
@@ -0,0 +1,54 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>war</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>jsondemos-jaxrs</artifactId>
+
+    <name>jsondemos-jaxrs</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.ws.rs</groupId>
+            <artifactId>jakarta.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+            </plugin>
+        </plugins>
+        <finalName>jsondemos-jaxrs</finalName>
+    </build>
+</project>
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java
new file mode 100644
index 0000000..8df6440
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.*;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * JsonArray as parameter and return type for a Jakarta RESTful Web Services resource
+ *
+ * @author Jitendra Kotamraju
+ */
+@Path("/array")
+public class ArrayResource {
+    private static final JsonBuilderFactory bf = Json.createBuilderFactory(null);
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public JsonArray doGet() {
+        return bf.createArrayBuilder()
+                .add(bf.createObjectBuilder()
+                    .add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(bf.createObjectBuilder()
+                    .add("type", "fax")
+                    .add("number", "646 555-4567"))
+                .build();
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void doPost(JsonArray structure) {
+        System.out.println(structure);
+    }
+
+}
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java
new file mode 100644
index 0000000..da8d87f
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.stream.JsonGenerator;
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Jakarta RESTful Web Services Demo Application
+ *
+ * @author Jitendra Kotamraju
+ */
+@ApplicationPath("/")
+public class DemoApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> set = new HashSet<>();
+        set.add(ParserResource.class);
+        set.add(GeneratorResource.class);
+        set.add(ObjectResource.class);
+        set.add(ArrayResource.class);
+        set.add(StructureResource.class);
+
+        return set;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return new HashMap<String, Object>() {{
+            put(JsonGenerator.PRETTY_PRINTING, true);
+        }};
+    }
+}
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java
new file mode 100644
index 0000000..705a724
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.StreamingOutput;
+import java.io.OutputStream;
+
+/**
+ * Writes wiki's JSON example in a streaming fashion using JsonGenerator
+ *
+ * @author Jitendra Kotamraju
+ */
+@Path("/generator")
+public class GeneratorResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public StreamingOutput doGet() {
+        return new StreamingOutput() {
+            public void write(OutputStream os) {
+                writeWikiExample(os);
+            }
+        };
+    }
+
+    // Writes wiki example JSON in a streaming fashion
+    private void writeWikiExample(OutputStream os) {
+        try(JsonGenerator gene = Json.createGenerator(os)) {
+            gene.writeStartObject()
+                .write("firstName", "John")
+                .write("lastName", "Smith")
+                .write("age", 25)
+                .writeStartObject("address")
+                    .write("streetAddress", "21 2nd Street")
+                    .write("city", "New York")
+                    .write("state", "NY")
+                    .write("postalCode", "10021")
+                .writeEnd()
+                .writeStartArray("phoneNumber")
+                    .writeStartObject()
+                        .write("type", "home")
+                        .write("number", "212 555-1234")
+                    .writeEnd()
+                    .writeStartObject()
+                        .write("type", "fax")
+                        .write("number", "646 555-4567")
+                    .writeEnd()
+                .writeEnd()
+            .writeEnd();
+        }
+    }
+
+}
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java
new file mode 100644
index 0000000..c12eb35
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.*;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * JsonObject as parameter and return type for a
+ * Jakarta RESTful Web Services resource
+ * Writes a person's representation as JSON using JsonObject
+ *
+ * @author Jitendra Kotamraju
+ */
+@Path("/object")
+public class ObjectResource {
+    private static final JsonBuilderFactory bf = Json.createBuilderFactory(null);
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public JsonObject doGet() {
+        return bf.createObjectBuilder()
+            .add("firstName", "John")
+            .add("lastName", "Smith")
+            .add("age", 25)
+            .add("address", bf.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021"))
+            .add("phoneNumber", bf.createArrayBuilder()
+                .add(bf.createObjectBuilder()
+                    .add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(bf.createObjectBuilder()
+                    .add("type", "fax")
+                    .add("number", "646 555-4567")))
+            .build();
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void doPost(JsonObject structure) {
+        System.out.println(structure);
+    }
+
+}
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java
new file mode 100644
index 0000000..6809a4d
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.StreamingOutput;
+import java.io.*;
+import java.net.URL;
+
+/**
+ * Filters JSON from flicker photo search REST API
+ *
+ * {
+ *    photos : {
+ *        photo: [
+ *           { id: "9889087315", secret: "40aeb70c83", server: "3818",farm: 4, ..},
+ *           { id: "9889087315", secret: "40aeb70c83", server: "3818",farm: 4, ..}
+ *           ...
+ *        ],
+ *        ...
+ *    }
+ * }
+ *
+ * @author Jitendra Kotamraju
+ */
+@Path("/parser")
+public class ParserResource {
+
+    @GET
+    @Produces("text/html")
+    public StreamingOutput doGet() {
+        return new StreamingOutput() {
+            public void write(OutputStream os) throws IOException {
+                writeFlickerFeed(os);
+            }
+        };
+    }
+
+    private void writeFlickerFeed(OutputStream os) throws IOException {
+        URL url = new URL("http://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=221160312e1c22ec60ecf336951b0e77&format=json&nojsoncallback=1&per_page=20");
+        try(InputStream is = url.openStream();
+            JsonParser parser = Json.createParser(is);
+            PrintWriter ps = new PrintWriter(new OutputStreamWriter(os, "UTF-8"))) {
+            String id = null;
+            String server = null;
+            String secret = null;
+
+            ps.println("<html><body>");
+            while(parser.hasNext()) {
+                Event e = parser.next();
+                if (e == Event.KEY_NAME) {
+                    String str = parser.getString();
+                    switch (str) {
+                        case "id" :
+                            parser.next();
+                            id = parser.getString();
+                            break;
+                        case "farm" :
+                            parser.next();
+                            String farm = parser.getString();
+                            ps.println("<img src=\"http://farm"+farm+".staticflickr.com/"+server+"/"+id+"_"+secret+".jpg\">");
+                            break;
+                        case "server" :
+                            parser.next();
+                            server = parser.getString();
+                            break;
+                        case "secret" :
+                            parser.next();
+                            secret = parser.getString();
+                            break;
+                    }
+                }
+            }
+            ps.println("</body></html>");
+            ps.flush();
+        }
+	}
+
+}
diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java
new file mode 100644
index 0000000..b1ce73c
--- /dev/null
+++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jaxrs;
+
+import jakarta.json.*;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * JsonStructure as parameter and return type for a
+ * Jakarta RESTful Web Services resource
+ *
+ * @author Jitendra Kotamraju
+ */
+@Path("/structure")
+public class StructureResource {
+    private static final JsonBuilderFactory bf = Json.createBuilderFactory(null);
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public JsonStructure doGet() {
+        return bf.createObjectBuilder()
+            .add("firstName", "John")
+            .add("lastName", "Smith")
+            .add("age", 25)
+            .add("address", bf.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021"))
+            .add("phoneNumber", bf.createArrayBuilder()
+                .add(bf.createObjectBuilder()
+                    .add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(bf.createObjectBuilder()
+                    .add("type", "fax")
+                    .add("number", "646 555-4567")))
+            .build();
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void doPost(JsonStructure structure) {
+        System.out.println(structure);
+    }
+
+}
diff --git a/demos/jsonpointer/pom.xml b/demos/jsonpointer/pom.xml
new file mode 100644
index 0000000..d32c252
--- /dev/null
+++ b/demos/jsonpointer/pom.xml
@@ -0,0 +1,105 @@
+<!--
+
+    Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>jar</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>jsondemos-jsonpointer</artifactId>
+
+    <properties>
+        <main.class>org.glassfish.jsondemos.jsonpointer.JsonpointerDemo</main.class>
+        <modules.directory>${project.build.directory}/modules</modules.directory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+            <classifier>module</classifier>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <configuration>
+                    <stripVersion>true</stripVersion>
+                    <outputDirectory>${modules.directory}</outputDirectory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>get-dependencies</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>get-project-artifact</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>${project.groupId}</groupId>
+                                    <artifactId>${project.artifactId}</artifactId>
+                                    <version>${project.version}</version>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <executable>java</executable>
+                    <arguments>
+                        <argument>--module-path</argument>
+                        <argument>${modules.directory}</argument>
+                        <argument>-m</argument>
+                        <argument>org.glassfish.jakarta.json.demos.jsonpointer/${main.class}</argument>
+                    </arguments>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>example-jsonpointer</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/demos/jsonpointer/src/main/java/module-info.java b/demos/jsonpointer/src/main/java/module-info.java
new file mode 100644
index 0000000..b14fa4a
--- /dev/null
+++ b/demos/jsonpointer/src/main/java/module-info.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+module org.glassfish.jakarta.json.demos.jsonpointer {
+    requires jakarta.json;
+}
diff --git a/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java b/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java
new file mode 100644
index 0000000..4236a0f
--- /dev/null
+++ b/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.jsonpointer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+/**
+ * JsonPointer (http://tools.ietf.org/html/rfc6901) demo with object model API
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonpointerDemo {
+
+    public static void main(String... args) throws Exception {
+        testWiki();
+        testPointer();
+        System.out.println("Both tests PASSED !!!");
+    }
+
+    private static void testWiki() throws IOException {
+        try (InputStream is = JsonpointerDemo.class.getResourceAsStream("/wiki.json");
+             JsonReader rdr = Json.createReader(is)) {
+            JsonObject person = rdr.readObject();
+
+            assertEquals("NY", getString(person, "/address/state"));
+            assertEquals("212 555-1234", getString(person, "/phoneNumber/0/number"));
+        }
+    }
+
+    private static void testPointer() throws IOException {
+        try (InputStream is = JsonpointerDemo.class.getResourceAsStream("/jsonpointer.json");
+             JsonReader rdr = Json.createReader(is)) {
+            JsonObject root = rdr.readObject();
+
+            assertEquals(root, get(root, ""));
+            assertEquals(root.get("foo"), get(root, "/foo"));
+            assertEquals(root.getJsonArray("foo").get(0), get(root, "/foo/0"));
+            assertEquals(root.get(""), get(root, "/"));
+            assertEquals(root.get("a/b"), get(root, "/a~1b"));
+            assertEquals(root.get("c%d"), get(root, "/c%d"));
+            assertEquals(root.get("e^f"), get(root, "/e^f"));
+            assertEquals(root.get("k\"l"), get(root, "/k\"l"));
+            assertEquals(root.get("i\\j"), get(root, "/i\\j"));
+            assertEquals(root.get(" "), get(root, "/ "));
+            assertEquals(root.get("m~n"), get(root, "/m~0n"));
+
+            // Adding a parent to current root and try with it
+            JsonObject doc = Json.createObjectBuilder().add("doc", root).build();
+            root = doc.getJsonObject("doc");
+            assertEquals(doc, get(doc, ""));
+            assertEquals(root.get("foo"), get(doc, "/doc/foo"));
+            assertEquals(root.getJsonArray("foo").get(0), get(doc, "/doc/foo/0"));
+            assertEquals(root.get(""), get(doc, "/doc/"));
+            assertEquals(root.get("a/b"), get(doc, "/doc/a~1b"));
+            assertEquals(root.get("c%d"), get(doc, "/doc/c%d"));
+            assertEquals(root.get("e^f"), get(doc, "/doc/e^f"));
+            assertEquals(root.get("k\"l"), get(doc, "/doc/k\"l"));
+            assertEquals(root.get("i\\j"), get(doc, "/doc/i\\j"));
+            assertEquals(root.get(" "), get(doc, "/doc/ "));
+            assertEquals(root.get("m~n"), get(doc, "/doc/m~0n"));
+        }
+    }
+
+    private static String getString(JsonValue root, String pointer) {
+        return ((JsonString)get(root, pointer)).getString();
+    }
+
+    private static JsonValue get(JsonValue root, String pointer) {
+        if (pointer.isEmpty()) {
+            return root;
+        }
+        if (pointer.charAt(0) != '/') {
+            throw new IllegalArgumentException(
+                    "JsonPointer "+pointer+" doesn't start with /");
+        }
+
+        StringBuilder referenceToken = new StringBuilder();
+        for(int i=1; i < pointer.length(); i++) {   // 1 to skip first /
+            char ch = pointer.charAt(i);
+            if (ch == '/') {
+                return get(newRoot(root, referenceToken.toString()), pointer.substring(i));
+            } else if (ch == '~') {
+                // handle escaping ~0, ~1
+                if (i+1 == pointer.length()) {
+                    throw new IllegalArgumentException("Illegal escaping: expected ~0 or ~1, but got only ~ in pointer="+pointer);
+                }
+                ch = pointer.charAt(++i);
+                if (ch == '0') {
+                    referenceToken.append('~');
+                } else if (ch == '1') {
+                    referenceToken.append('/');
+                } else {
+                    throw new IllegalArgumentException("Illegal escaping: expected ~0 or ~1, but got ~"+ch+" in pointer="+pointer);
+                }
+            } else {
+                referenceToken.append(ch);
+            }
+        }
+        return newRoot(root, referenceToken.toString());
+    }
+
+    private static JsonValue newRoot(JsonValue root, String referenceToken) {
+        if (root instanceof JsonObject) {
+            return ((JsonObject)root).get(referenceToken);
+        } else if (root instanceof JsonArray) {
+            return ((JsonArray)root).get(Integer.parseInt(referenceToken));
+        }
+        throw new IllegalArgumentException("Illegal reference token="+referenceToken+" for value="+root);
+    }
+
+    private static void assertEquals(JsonValue exp, JsonValue got) {
+        if (exp != got) {
+            throw new RuntimeException("Expected = "+exp+" but got = "+got);
+        }
+    }
+
+    private static void assertEquals(String exp, String got) {
+        if (!exp.equals(got)) {
+            throw new RuntimeException("Expected = "+exp+" but got = "+got);
+        }
+    }
+
+}
diff --git a/demos/jsonpointer/src/main/resources/jsonpointer.json b/demos/jsonpointer/src/main/resources/jsonpointer.json
new file mode 100644
index 0000000..937a098
--- /dev/null
+++ b/demos/jsonpointer/src/main/resources/jsonpointer.json
@@ -0,0 +1,12 @@
+{
+  "foo": ["bar", "baz"],
+  "": 0,
+  "a/b": 1,
+  "c%d": 2,
+  "e^f": 3,
+  "g|h": 4,
+  "i\\j": 5,
+  "k\"l": 6,
+  " ": 7,
+  "m~n": 8
+}
diff --git a/demos/jsonpointer/src/main/resources/wiki.json b/demos/jsonpointer/src/main/resources/wiki.json
new file mode 100644
index 0000000..17bb193
--- /dev/null
+++ b/demos/jsonpointer/src/main/resources/wiki.json
@@ -0,0 +1,21 @@
+{
+     "firstName": "John",
+     "lastName": "Smith",
+     "age": 25,
+     "address": {
+         "streetAddress": "21 2nd Street",
+         "city": "New York",
+         "state": "NY",
+         "postalCode": "10021"
+     },
+     "phoneNumber": [
+         {
+           "type": "home",
+           "number": "212 555-1234"
+         },
+         {
+           "type": "fax",
+           "number": "646 555-4567"
+         }
+     ]
+}
diff --git a/demos/pom.xml b/demos/pom.xml
new file mode 100644
index 0000000..6107d19
--- /dev/null
+++ b/demos/pom.xml
@@ -0,0 +1,41 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>pom</packaging>
+    <url>http://maven.apache.org</url>
+    <groupId>org.glassfish.jsonp</groupId>
+    <artifactId>demos</artifactId>
+
+    <properties>
+        <legal.doc.source>${basedir}/..</legal.doc.source>
+    </properties>
+
+    <modules>
+        <module>jaxrs</module>
+        <module>twitter</module>
+        <module>facebook</module>
+        <module>jsonpointer</module>
+        <module>servlet</module>
+        <module>customprovider-jdk9</module>
+    </modules>
+
+</project>
diff --git a/demos/servlet/pom.xml b/demos/servlet/pom.xml
new file mode 100644
index 0000000..35a337a
--- /dev/null
+++ b/demos/servlet/pom.xml
@@ -0,0 +1,54 @@
+<!--
+
+    Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>war</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>jsondemos-servlet</artifactId>
+
+    <name>jsondemos-servlet</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+            </plugin>
+        </plugins>
+        <finalName>jsondemos-servlet</finalName>
+    </build>
+</project>
diff --git a/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java b/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java
new file mode 100644
index 0000000..9cac18f
--- /dev/null
+++ b/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.servlet;
+
+import java.io.IOException;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonWriter;
+import jakarta.json.JsonWriterFactory;
+
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * Writes a JsonArray using HttpServletResponse#getWriter
+ * http://localhost:8080/jsondemos-servlet/array
+ *
+ * Writes a JsonArray using HttpServletResponse#getOutputStream
+ * http://localhost:8080/jsondemos-servlet/array?stream
+ *
+ *
+ * @author Jitendra Kotamraju
+ */
+@WebServlet("/array")
+public class ArrayServlet extends HttpServlet {
+    private static final JsonBuilderFactory bf = Json.createBuilderFactory(null);
+    private static final JsonWriterFactory wf = Json.createWriterFactory(null);
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+        JsonArray array = bf.createArrayBuilder()
+                .add(bf.createObjectBuilder()
+                    .add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(bf.createObjectBuilder()
+                    .add("type", "fax")
+                    .add("number", "646 555-4567"))
+                .build();
+        res.setStatus(HttpServletResponse.SC_OK);
+        res.setContentType("application/json");
+        res.setCharacterEncoding("UTF-8");
+
+        String q = req.getQueryString();
+        boolean isStream = q != null && q.equals("stream");
+        JsonWriter writer = isStream
+                ? wf.createWriter(res.getOutputStream())
+                : wf.createWriter(res.getWriter());
+        writer.write(array);
+        // not closing writer intentionally
+    }
+
+}
diff --git a/demos/twitter/pom.xml b/demos/twitter/pom.xml
new file mode 100644
index 0000000..8ce39fa
--- /dev/null
+++ b/demos/twitter/pom.xml
@@ -0,0 +1,120 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>demos</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>jar</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>jsondemos-twitter</artifactId>
+
+    <properties>
+        <main.class>org.glassfish.jsondemos.twitter.TwitterObjectSearch</main.class>
+        <!--
+        <main.class>org.glassfish.jsondemos.twitter.TwitterStreamSearch</main.class>
+        -->
+        <modules.directory>${project.build.directory}/modules</modules.directory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+            <classifier>module</classifier>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <configuration>
+                    <stripVersion>true</stripVersion>
+                    <outputDirectory>${modules.directory}</outputDirectory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>get-dependencies</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <excludeArtifactIds>jakarta.activation-api</excludeArtifactIds>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>get-project-artifact</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>${project.groupId}</groupId>
+                                    <artifactId>${project.artifactId}</artifactId>
+                                    <version>${project.version}</version>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <executable>java</executable>
+                    <arguments>
+                        <argument>--module-path</argument>
+                        <argument>${modules.directory}</argument>
+                        <argument>-m</argument>
+                        <argument>org.glassfish.jakarta.json.demos.twitter/${main.class}</argument>
+                    </arguments>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>example-twitter</id>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/demos/twitter/src/main/java/module-info.java b/demos/twitter/src/main/java/module-info.java
new file mode 100644
index 0000000..1996e3e
--- /dev/null
+++ b/demos/twitter/src/main/java/module-info.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+module org.glassfish.jakarta.json.demos.twitter {
+    requires jakarta.xml.bind;
+    requires jakarta.json;
+}
diff --git a/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java
new file mode 100644
index 0000000..6538140
--- /dev/null
+++ b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.twitter;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import jakarta.json.*;
+import jakarta.xml.bind.DatatypeConverter;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.*;
+
+/**
+ * Parses JSON from twitter search REST API using object model API.
+ * JSON would like :
+ *
+ * {
+ "   statuses": [
+ *     { ..., "user" : { "name" : "xxx", ...}, "text: "yyy", ... },
+ *     { ..., "user" : { "name" : "ppp", ...}, "text: "qqq", ... },
+ *     ...
+ *   ],
+ *   ...
+ * }
+ *
+ * This codes writes the tweets to output as follows:
+ * xxx: yyy
+ * --------
+ * ppp: qqq
+ * --------
+ *
+ * @author Jitendra Kotamraju
+ */
+public class TwitterObjectSearch {
+
+    public static void main(String... args) throws Exception {
+        try (InputStream is = getSearchStream();
+             JsonReader rdr = Json.createReader(is)) {
+
+            JsonObject obj = rdr.readObject();
+            JsonArray results = obj.getJsonArray("statuses");
+            for (JsonObject result : results.getValuesAs(JsonObject.class)) {
+                System.out.print(result.getJsonObject("user").getString("name", "anonymous"));
+                System.out.print(": ");
+                System.out.println(result.get("text"));
+                System.out.println("-----------");
+            }
+
+//            All the tweets are collected into Stream<String> and printed
+//            obj.getJsonArray("statuses").getValuesAs(JsonObject.class)
+//                    .stream()
+//                    .map(v -> v.getString("text"))
+//                    .forEach(s -> { System.out.println(s); } );
+        }
+    }
+
+    static InputStream getSearchStream() throws Exception {
+        final String searchStr = "#javaone";
+        String searchUrl = "https://api.twitter.com/1.1/search/tweets.json";
+
+        Properties config = new Properties();
+        config.load(TwitterObjectSearch.class.getResourceAsStream(
+                "/twitterconfig.properties"));
+
+        final String consumerKey = (String)config.get("consumer-key");
+        final String consumerSecret = (String)config.get("consumer-secret");
+        final String accessToken = (String)config.get("access-token");
+        final String accessTokenSecret = (String)config.get("access-token-secret");
+        final int timestamp = (int)(System.currentTimeMillis()/1000);
+
+        Map<String, String> map = new TreeMap<String, String>() {{
+            put("count", "100");
+            put("oauth_consumer_key", consumerKey);
+            put("oauth_nonce", "4b25256957d75b6370f33a4501dc5e7e"); // TODO
+            put("oauth_signature_method", "HMAC-SHA1");
+            put("oauth_timestamp", ""+timestamp);
+            put("oauth_token", accessToken);
+            put("oauth_version", "1.0");
+            put("q", searchStr);
+        }};
+
+        // Builds param string
+        StringBuilder paramsBuilder = new StringBuilder();
+        boolean first = true;
+        for(Map.Entry<String, String> e : map.entrySet()) {
+            if (!first) {
+                paramsBuilder.append('&');
+            }
+            first = false;
+            paramsBuilder.append(e.getKey());
+            paramsBuilder.append("=");
+            paramsBuilder.append(URLEncoder.encode(e.getValue(), "UTF-8"));
+        }
+        String paramsString = paramsBuilder.toString();
+
+        // builds signature string
+        StringBuilder signatureBuilder = new StringBuilder();
+        signatureBuilder.append("GET");
+        signatureBuilder.append('&');
+        signatureBuilder.append(URLEncoder.encode(searchUrl, "UTF-8"));
+        signatureBuilder.append('&');
+        signatureBuilder.append(URLEncoder.encode(paramsString, "UTF-8"));
+        String signatureBasedString = signatureBuilder.toString();
+
+        // Create authorization signature
+        Mac m = Mac.getInstance("HmacSHA1");
+        m.init(new SecretKeySpec((consumerSecret+"&"+accessTokenSecret).getBytes(), "HmacSHA1"));
+        m.update(signatureBasedString.getBytes());
+        byte[] res = m.doFinal();
+        final String oauthSig = URLEncoder.encode(DatatypeConverter.printBase64Binary(res), "UTF-8");
+        map.put("oauth_signature", oauthSig);
+        map.remove("count");
+        map.remove("q");
+
+        // Build Authorization header
+        StringBuilder authorizationBuilder = new StringBuilder();
+        authorizationBuilder.append("OAuth ");
+        first = true;
+        for(Map.Entry<String, String> e : map.entrySet()) {
+            if (!first) {
+                authorizationBuilder.append(',');
+                authorizationBuilder.append(' ');
+            }
+            first = false;
+            authorizationBuilder.append(e.getKey());
+            authorizationBuilder.append('=');
+            authorizationBuilder.append('"');
+            authorizationBuilder.append(e.getValue());
+            authorizationBuilder.append('"');
+        }
+
+        // Gets the search stream
+        URL url = new URL(searchUrl+"?q="+URLEncoder.encode(searchStr, "UTF-8")+
+                "&count=100");
+        HttpURLConnection con = (HttpURLConnection)url.openConnection();
+        con.addRequestProperty("Authorization", authorizationBuilder.toString());
+        return con.getInputStream();
+    }
+
+}
diff --git a/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java
new file mode 100644
index 0000000..1271655
--- /dev/null
+++ b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jsondemos.twitter;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import java.io.*;
+
+/**
+ * Parses JSON from twitter search REST API using streaming API.
+ * JSON would like :
+ *
+ * {
+ "   statuses": [
+ *     { ..., "user" : { "name" : "xxx", ...}, "text: "yyy", ... },
+ *     { ..., "user" : { "name" : "ppp", ...}, "text: "qqq", ... },
+ *     ...
+ *   ],
+ *   ...
+ * }
+ *
+ * This codes writes the tweets to output as follows:
+ * xxx: yyy
+ * --------
+ * ppp: qqq
+ * --------
+ *
+ * TODO need to do better, also the last tweet is repeated !
+ *
+ * @author Jitendra Kotamraju
+ */
+public class TwitterStreamSearch {
+
+    public static void main(String... args) throws Exception {
+        try (InputStream is = TwitterObjectSearch.getSearchStream();
+             JsonParser parser = Json.createParser(is)) {
+            int depth = 0;
+            String name = null;
+            String text = null;
+            while (parser.hasNext()) {
+                Event e = parser.next();
+                if (e == Event.KEY_NAME) {
+                    switch (parser.getString()) {
+                        case "name":
+                            if (depth == 3) {
+                            parser.next();
+                            name = parser.getString();
+                            }
+                            break;
+                        case "text":
+                            if (depth == 2) {
+                            parser.next();
+                            text = parser.getString();
+                            }
+                            break;
+                    }
+                } else if (e == Event.START_OBJECT) {
+                    ++depth;
+                } else if (e == Event.END_OBJECT) {
+                    --depth;
+                    if (depth == 1) {
+                        System.out.println(name+": "+text);
+                        System.out.println("-----------");
+
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/demos/twitter/src/main/resources/twitterconfig.properties b/demos/twitter/src/main/resources/twitterconfig.properties
new file mode 100644
index 0000000..87b5d72
--- /dev/null
+++ b/demos/twitter/src/main/resources/twitterconfig.properties
@@ -0,0 +1,16 @@
+#
+# Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+
+consumer-key=
+consumer-secret=
+access-token=
+access-token-secret=
+
diff --git a/etc/config/copyright-exclude b/etc/config/copyright-exclude
new file mode 100644
index 0000000..e7f6f88
--- /dev/null
+++ b/etc/config/copyright-exclude
@@ -0,0 +1,8 @@
+.json
+.md
+speclicense.html
+MANIFEST.MF
+/META-INF/services/
+/etc/config/copyright-exclude
+/etc/config/copyright.txt
+/bundles/ri/src/main/resources/README.txt
diff --git a/etc/config/copyright.txt b/etc/config/copyright.txt
new file mode 100644
index 0000000..9c347b6
--- /dev/null
+++ b/etc/config/copyright.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) YYYY Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
diff --git a/etc/config/exclude.xml b/etc/config/exclude.xml
new file mode 100644
index 0000000..7380f9d
--- /dev/null
+++ b/etc/config/exclude.xml
@@ -0,0 +1,14 @@
+<!--
+
+    Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<FindBugsFilter>
+</FindBugsFilter>
diff --git a/gf/customprovider/pom.xml b/gf/customprovider/pom.xml
new file mode 100644
index 0000000..f472eb6
--- /dev/null
+++ b/gf/customprovider/pom.xml
@@ -0,0 +1,38 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>providers</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>war</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>customprovider</artifactId>
+
+    <name>customprovider</name>
+
+    <build>
+        <finalName>customprovider</finalName>
+    </build>
+</project>
diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java
new file mode 100644
index 0000000..c11f013
--- /dev/null
+++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.customprovider;
+
+import jakarta.json.JsonException;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerator;
+import java.io.IOException;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class TestGenerator implements JsonGenerator {
+    private final Writer writer;
+
+    public TestGenerator(Writer writer) {
+        this.writer = writer;
+    }
+
+    @Override
+    public void flush() {
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String fieldValue) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        try {
+            writer.write("[");
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        return null;
+    }
+
+
+    @Override
+    public JsonGenerator write(int value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        return null;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        try {
+            writer.write("]");
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+        return this;
+    }
+
+    @Override
+    public void close() {
+        try {
+            writer.close();
+        } catch(IOException ioe) {
+            throw new JsonException("I/O error", ioe);
+        }
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        return null;
+    }
+
+}
diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java
new file mode 100644
index 0000000..ee1ffcb
--- /dev/null
+++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.customprovider;
+
+import jakarta.json.*;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class TestProvider extends JsonProvider {
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return new TestGenerator(writer);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return null;
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return null;
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return null;
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return null;
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return null;
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return null;
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return null;
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
+        return null;
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return null;
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return null;
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(Map<String, ?> config) {
+        return null;
+    }
+
+
+}
diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java
new file mode 100644
index 0000000..38bd1d4
--- /dev/null
+++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.customprovider;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+@WebServlet("/json")
+public class TestServlet extends HttpServlet {
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException {
+        try {
+            res.setStatus(200);
+            res.setContentType("application/json");
+            OutputStream os = res.getOutputStream();
+            JsonGenerator generator = Json.createGenerator(new OutputStreamWriter(os));
+            if (!(generator instanceof TestGenerator)) {
+                throw new RuntimeException("MyGenerator is not picked up");
+            }
+            generator.writeStartArray().writeEnd();
+            generator.close();
+            os.close();
+        } catch (IOException ioe) {
+            throw new ServletException(ioe);
+        }
+    }
+
+}
diff --git a/gf/customprovider/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider b/gf/customprovider/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
new file mode 100644
index 0000000..0647df2
--- /dev/null
+++ b/gf/customprovider/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
@@ -0,0 +1 @@
+org.glassfish.json.customprovider.TestProvider
diff --git a/gf/defaultprovider/pom.xml b/gf/defaultprovider/pom.xml
new file mode 100644
index 0000000..e1199af
--- /dev/null
+++ b/gf/defaultprovider/pom.xml
@@ -0,0 +1,38 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jsonp</groupId>
+        <artifactId>providers</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>war</packaging>
+    <url>http://maven.apache.org</url>
+    <artifactId>defaultprovider</artifactId>
+
+    <name>defaultprovider</name>
+
+    <build>
+        <finalName>defaultprovider</finalName>
+    </build>
+</project>
diff --git a/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java b/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java
new file mode 100644
index 0000000..ac58dd5
--- /dev/null
+++ b/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.defaultprovider;
+
+import jakarta.servlet.annotation.*;
+import jakarta.servlet.http.*;
+import jakarta.servlet.*;
+import java.io.IOException;
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import java.io.*;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+@WebServlet("/json")
+public class TestServlet extends HttpServlet {
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException {
+        try {
+            res.setStatus(200);
+            res.setContentType("application/json");
+            OutputStream os = res.getOutputStream();
+            JsonGenerator generator = Json.createGenerator(os);
+            generator.writeStartArray().writeEnd();
+            generator.close();
+        } catch(IOException ioe) {
+            throw new ServletException(ioe);
+        }
+    }
+
+}
diff --git a/gf/pom.xml b/gf/pom.xml
new file mode 100644
index 0000000..c85e902
--- /dev/null
+++ b/gf/pom.xml
@@ -0,0 +1,59 @@
+<!--
+
+    Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <packaging>pom</packaging>
+    <url>http://maven.apache.org</url>
+    <groupId>org.glassfish.jsonp</groupId>
+    <artifactId>providers</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+            </plugin>
+        </plugins>
+        <finalName>customprovider</finalName>
+    </build>
+    <modules>
+        <module>customprovider</module>
+        <module>defaultprovider</module>
+    </modules>
+</project>
diff --git a/impl-tck/pom.xml b/impl-tck/pom.xml
new file mode 100644
index 0000000..7f24ba8
--- /dev/null
+++ b/impl-tck/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>jakarta.json-tck</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>tck-tests</module>
+        <module>tck-tests-plugability</module>
+    </modules>
+
+    <properties>
+        <jsonp-api.version>2.0.0-SNAPSHOT</jsonp-api.version>
+        <jsonp-impl.version>2.0.0-SNAPSHOT</jsonp-impl.version>
+        <jsonp-tck.version>2.0.0-SNAPSHOT</jsonp-tck.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <version>${jsonp-api.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jakarta.json</artifactId>
+            <version>${jsonp-impl.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.container</groupId>
+            <artifactId>arquillian-weld-embedded</artifactId>
+            <version>2.0.1.Final</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.se</groupId>
+            <artifactId>weld-se</artifactId>
+            <version>2.4.3.Final</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/impl-tck/tck-tests-plugability/pom.xml b/impl-tck/tck-tests-plugability/pom.xml
new file mode 100644
index 0000000..c3e035d
--- /dev/null
+++ b/impl-tck/tck-tests-plugability/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>jakarta.json-tck</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jakarta.json-tck-tests-plugability</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-tests-plugability</artifactId>
+            <version>${jsonp-tck.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-common</artifactId>
+            <version>${jsonp-tck.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M4</version>
+                <configuration>
+                    <dependenciesToScan>
+                        <dependency>jakarta.json:jakarta.json-tck-tests-plugability</dependency>
+                    </dependenciesToScan>
+                    <trimStackTrace>false</trimStackTrace>
+                    <failIfNoTests>true</failIfNoTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/impl-tck/tck-tests/pom.xml b/impl-tck/tck-tests/pom.xml
new file mode 100644
index 0000000..006e732
--- /dev/null
+++ b/impl-tck/tck-tests/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>jakarta.json-tck</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jakarta.json-tck-tests</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-tests</artifactId>
+            <version>${jsonp-tck.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-common</artifactId>
+            <version>${jsonp-tck.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M4</version>
+                <configuration>
+                    <dependenciesToScan>
+                        <dependency>jakarta.json:jakarta.json-tck-tests</dependency>
+                    </dependenciesToScan>
+                    <trimStackTrace>false</trimStackTrace>
+                    <failIfNoTests>true</failIfNoTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/impl/pom.xml b/impl/pom.xml
new file mode 100644
index 0000000..3bfd523
--- /dev/null
+++ b/impl/pom.xml
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>jakarta.json</artifactId>
+    <packaging>bundle</packaging>
+    <version>2.0.1</version>
+    <name>JSON-P Default Provider</name>
+    <description>Default provider for Jakarta JSON Processing</description>
+    <url>https://github.com/eclipse-ee4j/jsonp</url>
+
+    <properties>
+        <packages.private>org.glassfish.json</packages.private>
+        <packages.export>jakarta.json.*,org.glassfish.json.api</packages.export>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.glassfish.build</groupId>
+                <artifactId>spec-version-maven-plugin</artifactId>
+                <configuration>
+                    <spec>
+                        <nonFinal>${non.final}</nonFinal>
+                        <jarType>impl</jarType>
+                        <specVersion>${spec_version}</specVersion>
+                        <newSpecVersion>${new_spec_version}</newSpecVersion>
+                        <specImplVersion>${new_spec_impl_version}</specImplVersion>
+                        <implVersion>${impl_version}</implVersion>
+                        <newImplVersion>${new_impl_version}</newImplVersion>
+                        <apiPackage>${api_package}</apiPackage>
+                        <implNamespace>${impl_namespace}</implNamespace>
+                    </spec>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>set-spec-properties</goal>
+                            <!-- TODO:
+                            glassfish-spec-version-maven-plugin needs to be updated
+                            in order to check 'jakarta.' prefixed values in manifest entries
+                            -->
+                            <!--<goal>check-module</goal>-->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <configuration>
+                    <skipSource>true</skipSource>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sources-as-resources</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <resources>
+                                <resource>
+                                    <directory>src/main/java/org</directory>
+                                </resource>
+                            </resources>
+                            <outputDirectory>${project.build.directory}/sources/org</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack-module-info</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>unpack-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <includeArtifactIds>jakarta.json-api</includeArtifactIds>
+                            <outputDirectory>${project.build.directory}/binaries</outputDirectory>
+                            <includes>module-info.class</includes>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>unpack-client-sources</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>unpack</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>jakarta.json</groupId>
+                                    <artifactId>jakarta.json-api</artifactId>
+                                    <version>${jakarta.json-api.version}</version>
+                                    <classifier>sources</classifier>
+                                    <overWrite>false</overWrite>
+                                    <outputDirectory>${project.build.directory}/sources</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-source-jar</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <classifier>sources</classifier>
+                            <classesDirectory>${project.build.directory}/sources</classesDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!--
+              This plugin is reponsible for packaging artifacts
+              as OSGi bundles.  Please refer to
+              http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html
+              for more information about how to use this plugin.
+            -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>default-bundle</id>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                        <configuration>
+                            <instructions>
+                                <Bundle-Version>${spec.bundle.version}</Bundle-Version>
+                                <Bundle-SymbolicName>${spec.bundle.symbolic-name}</Bundle-SymbolicName>
+                                <Extension-Name>${spec.extension.name}</Extension-Name>
+                                <Implementation-Version>${spec.implementation.version}</Implementation-Version>
+                                <Specification-Vendor>Eclipse Foundation</Specification-Vendor>
+                                <Specification-Version>${spec.specification.version}</Specification-Version>
+                                <Export-Package>${packages.export}</Export-Package>
+                                <Private-Package>${packages.private}</Private-Package>
+                                <_donotcopy>.*services.*</_donotcopy>
+                                <!-- as of 5.1.1 this won't add appropriate uses jakarta.json.spi.JsonProvider; to module-info -->
+                                <!--<_jpms-module-info>jakarta.json</_jpms-module-info>-->
+                                <Include-Resource>{maven-resources},target/binaries/module-info.class</Include-Resource>
+                            </instructions>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>main-artifact-module</id>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                        <configuration>
+                            <classifier>module</classifier>
+                            <instructions>
+                                <Bundle-Version>${spec.bundle.version}</Bundle-Version>
+                                <Bundle-SymbolicName>${spec.bundle.symbolic-name}.module</Bundle-SymbolicName>
+                                <Extension-Name>${spec.extension.name}</Extension-Name>
+                                <Implementation-Version>${spec.implementation.version}</Implementation-Version>
+                                <Specification-Version>${spec.specification.version}</Specification-Version>
+                                <Export-Package>org.glassfish.json.api</Export-Package>
+                                <Private-Package>${packages.private}</Private-Package>
+                                <Include-Resource>{maven-resources},target/classes/module-info.class</Include-Resource>
+                            </instructions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <sourcepath>target/sources</sourcepath>
+                    <archive>
+                        <manifest>
+                            <addDefaultEntries>false</addDefaultEntries>
+                        </manifest>
+                    </archive>
+                    <release>11</release>
+                    <notimestamp>true</notimestamp>
+                    <docfilessubdirs>true</docfilessubdirs>
+                    <description>JSON Processing API documentation</description>
+                    <doctitle>JSON Processing API documentation</doctitle>
+                    <windowtitle>JSON Processing API documentation</windowtitle>
+                    <header><![CDATA[<br>JSON Processing API v${project.version}]]></header>
+                    <bottom><![CDATA[
+Comments to: <a href="mailto:jsonp-dev@eclipse.org">jsonp-dev@eclipse.org</a>.<br>
+Copyright &#169; 2019, 2020 Eclipse Foundation. All rights reserved.<br>
+Use is subject to <a href="{@docRoot}/doc-files/speclicense.html" target="_top">license terms</a>.]]>
+                    </bottom>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/impl/src/main/java/module-info.java b/impl/src/main/java/module-info.java
new file mode 100644
index 0000000..53be057
--- /dev/null
+++ b/impl/src/main/java/module-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+module org.glassfish.jakarta.json {
+    requires transitive jakarta.json;
+    exports org.glassfish.json.api;
+    provides jakarta.json.spi.JsonProvider with org.glassfish.json.JsonProviderImpl;
+}
diff --git a/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java b/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java
new file mode 100644
index 0000000..460c6f7
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * char[] pool that pool instances of char[] which are expensive to create.
+ *
+ * @author Jitendra Kotamraju
+ */
+class BufferPoolImpl implements BufferPool {
+
+    // volatile since multiple threads may access queue reference
+    private volatile WeakReference<ConcurrentLinkedQueue<char[]>> queue;
+
+    /**
+     * Gets a new object from the pool.
+     *
+     * <p>
+     * If no object is available in the pool, this method creates a new one.
+     *
+     * @return
+     *      always non-null.
+     */
+    @Override
+    public final char[] take() {
+        char[] t = getQueue().poll();
+        if (t==null)
+            return new char[4096];
+        return t;
+    }
+
+    private ConcurrentLinkedQueue<char[]> getQueue() {
+        WeakReference<ConcurrentLinkedQueue<char[]>> q = queue;
+        if (q != null) {
+            ConcurrentLinkedQueue<char[]> d = q.get();
+            if (d != null)
+                return d;
+        }
+
+        // overwrite the queue
+        ConcurrentLinkedQueue<char[]> d = new ConcurrentLinkedQueue<>();
+        queue = new WeakReference<>(d);
+
+        return d;
+    }
+
+    /**
+     * Returns an object back to the pool.
+     */
+    @Override
+    public final void recycle(char[] t) {
+        getQueue().offer(t);
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java
new file mode 100644
index 0000000..b0c3085
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.*;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * JsonArrayBuilder implementation
+ *
+ * @author Jitendra Kotamraju
+ * @author Kin-man Chung
+ */
+
+class JsonArrayBuilderImpl implements JsonArrayBuilder {
+    private ArrayList<JsonValue> valueList;
+    private final BufferPool bufferPool;
+
+    JsonArrayBuilderImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    JsonArrayBuilderImpl(JsonArray array, BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+        valueList = new ArrayList<>();
+        valueList.addAll(array);
+    }
+
+    JsonArrayBuilderImpl(Collection<?> collection, BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+        valueList = new ArrayList<>();
+        populate(collection);
+    }
+
+    @Override
+    public JsonArrayBuilder add(JsonValue value) {
+        validateValue(value);
+        addValueList(value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(String value) {
+        validateValue(value);
+        addValueList(new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(BigDecimal value) {
+        validateValue(value);
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(BigInteger value) {
+        validateValue(value);
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(long value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(double value) {
+        addValueList(JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(boolean value) {
+        addValueList(value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull() {
+        addValueList(JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(JsonObjectBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        addValueList(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(JsonArrayBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_ARRAY_BUILDER_NULL());
+        }
+        addValueList(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addAll(JsonArrayBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_ARRAY_BUILDER_NULL());
+        }
+        if (valueList == null) {
+            valueList = new ArrayList<>();
+        }
+        valueList.addAll(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, JsonValue value) {
+        validateValue(value);
+        addValueList(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, String value) {
+        validateValue(value);
+        addValueList(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, BigDecimal value) {
+        validateValue(value);
+        addValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, BigInteger value) {
+        validateValue(value);
+        addValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, int value) {
+        addValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, long value) {
+        addValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, double value) {
+        addValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, boolean value) {
+        addValueList(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull(int index) {
+        addValueList(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, JsonObjectBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        addValueList(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(int index, JsonArrayBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        addValueList(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, JsonValue value) {
+        validateValue(value);
+        setValueList(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, String value) {
+        validateValue(value);
+        setValueList(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, BigDecimal value) {
+        validateValue(value);
+        setValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, BigInteger value) {
+        validateValue(value);
+        setValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, int value) {
+        setValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, long value) {
+        setValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, double value) {
+        setValueList(index, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, boolean value) {
+        setValueList(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder setNull(int index) {
+        setValueList(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, JsonObjectBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        setValueList(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(int index, JsonArrayBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL());
+        }
+        setValueList(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder remove(int index) {
+        if (valueList == null) {
+            throw new IndexOutOfBoundsException(JsonMessages.ARRBUILDER_VALUELIST_NULL(index, 0));
+        }
+        valueList.remove(index);
+        return this;
+    }
+
+    @Override
+    public JsonArray build() {
+        List<JsonValue> snapshot;
+        if (valueList == null) {
+            snapshot = Collections.emptyList();
+        } else {
+            // Should we trim to minimize storage ?
+            // valueList.trimToSize();
+            snapshot = Collections.unmodifiableList(valueList);
+        }
+        valueList = null;
+        return new JsonArrayImpl(snapshot, bufferPool);
+    }
+
+    private void populate(Collection<?> collection) {
+        for (Object value : collection) {
+            if (value != null && value instanceof Optional) {
+                ((Optional<?>) value).ifPresent(v ->
+                        this.valueList.add(MapUtil.handle(v, bufferPool)));
+            } else {
+                this.valueList.add(MapUtil.handle(value, bufferPool));
+            }
+        }
+    }
+
+    private void addValueList(JsonValue value) {
+        if (valueList == null) {
+            valueList = new ArrayList<>();
+        }
+        valueList.add(value);
+    }
+
+    private void addValueList(int index, JsonValue value) {
+        if (valueList == null) {
+            valueList = new ArrayList<>();
+        }
+        valueList.add(index, value);
+    }
+
+    private void setValueList(int index, JsonValue value) {
+        if (valueList == null) {
+            throw new IndexOutOfBoundsException(JsonMessages.ARRBUILDER_VALUELIST_NULL(index, 0));
+        }
+        valueList.set(index, value);
+    }
+
+    private void validateValue(Object value) {
+        if (value == null) {
+            throw new NullPointerException(JsonMessages.ARRBUILDER_VALUE_NULL());
+        }
+    }
+
+    private static final class JsonArrayImpl extends AbstractList<JsonValue> implements JsonArray {
+        private final List<JsonValue> valueList;    // Unmodifiable
+        private final BufferPool bufferPool;
+        private int hashCode;
+
+        JsonArrayImpl(List<JsonValue> valueList, BufferPool bufferPool) {
+            this.valueList = valueList;
+            this.bufferPool = bufferPool;
+        }
+
+        @Override
+        public int size() {
+            return valueList.size();
+        }
+
+        @Override
+        public JsonObject getJsonObject(int index) {
+            return (JsonObject)valueList.get(index);
+        }
+
+        @Override
+        public JsonArray getJsonArray(int index) {
+            return (JsonArray)valueList.get(index);
+        }
+
+        @Override
+        public JsonNumber getJsonNumber(int index) {
+            return (JsonNumber)valueList.get(index);
+        }
+
+        @Override
+        public JsonString getJsonString(int index) {
+            return (JsonString)valueList.get(index);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
+            return (List<T>)valueList;
+        }
+
+        @Override
+        public String getString(int index) {
+            return getJsonString(index).getString();
+        }
+
+        @Override
+        public String getString(int index, String defaultValue) {
+            try {
+                return getString(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public int getInt(int index) {
+            return getJsonNumber(index).intValue();
+        }
+
+        @Override
+        public int getInt(int index, int defaultValue) {
+            try {
+                return getInt(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean getBoolean(int index) {
+            JsonValue jsonValue = get(index);
+            if (jsonValue == JsonValue.TRUE) {
+                return true;
+            } else if (jsonValue == JsonValue.FALSE) {
+                return false;
+            } else {
+                throw new ClassCastException();
+            }
+        }
+
+        @Override
+        public boolean getBoolean(int index, boolean defaultValue) {
+            try {
+                return getBoolean(index);
+            } catch (Exception e) {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean isNull(int index) {
+            return valueList.get(index).equals(JsonValue.NULL);
+        }
+
+        @Override
+        public ValueType getValueType() {
+            return ValueType.ARRAY;
+        }
+
+        @Override
+        public JsonValue get(int index) {
+            return valueList.get(index);
+        }
+
+        @Override
+        public int hashCode() {
+            if (hashCode == 0) {
+                hashCode = super.hashCode();
+            }
+            return hashCode;
+        }
+
+        @Override
+        public String toString() {
+            StringWriter sw = new StringWriter();
+            try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) {
+                jw.write(this);
+            }
+            return sw.toString();
+        }
+
+        @Override
+        public JsonArray asJsonArray() {
+            return this;
+        }
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java
new file mode 100644
index 0000000..7fde7a3
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import java.util.Collection;
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonObject;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObjectBuilder;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonBuilderFactoryImpl implements JsonBuilderFactory {
+    private final Map<String, ?> config;
+    private final BufferPool bufferPool;
+    private final boolean rejectDuplicateKeys;
+
+    JsonBuilderFactoryImpl(BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.config = Collections.emptyMap();
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys);
+    }
+ 
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject object) {
+        return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> object) {
+        return new JsonObjectBuilderImpl(object, bufferPool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray array) {
+        return new JsonArrayBuilderImpl(array, bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> collection) {
+        return new JsonArrayBuilderImpl(collection, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java
new file mode 100644
index 0000000..e347641
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonGeneratorFactoryImpl implements JsonGeneratorFactory {
+
+    private final boolean prettyPrinting;
+    private final Map<String, ?> config;    // unmodifiable map
+    private final BufferPool bufferPool;
+
+    JsonGeneratorFactoryImpl(Map<String, ?> config, boolean prettyPrinting,
+            BufferPool bufferPool) {
+        this.config = config;
+        this.prettyPrinting = prettyPrinting;
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(writer, bufferPool)
+                : new JsonGeneratorImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(out, bufferPool)
+                : new JsonGeneratorImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out, Charset charset) {
+        return prettyPrinting
+                ? new JsonPrettyGeneratorImpl(out, charset, bufferPool)
+                : new JsonGeneratorImpl(out, charset, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java
new file mode 100644
index 0000000..67c8e33
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java
@@ -0,0 +1,715 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonGenerationException;
+import jakarta.json.stream.JsonGenerator;
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonGeneratorImpl implements JsonGenerator {
+
+    private static final char[] INT_MIN_VALUE_CHARS = "-2147483648".toCharArray();
+    private static final int[] INT_CHARS_SIZE_TABLE = { 9, 99, 999, 9999, 99999,
+            999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE };
+
+    private static final char [] DIGIT_TENS = {
+            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+            '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
+            '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
+            '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
+            '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
+            '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
+            '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
+            '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
+            '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
+            '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
+    } ;
+
+    private static final char [] DIGIT_ONES = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    } ;
+
+    /**
+     * All possible chars for representing a number as a String
+     */
+    private static final char[] DIGITS = {
+            '0' , '1' , '2' , '3' , '4' , '5' ,
+            '6' , '7' , '8' , '9'
+    };
+
+    private static enum Scope {
+        IN_NONE,
+        IN_OBJECT,
+        IN_FIELD,
+        IN_ARRAY
+    }
+
+    private final BufferPool bufferPool;
+    private final Writer writer;
+    private Context currentContext = new Context(Scope.IN_NONE);
+    private final Deque<Context> stack = new ArrayDeque<>();
+
+    // Using own buffering mechanism as JDK's BufferedWriter uses synchronized
+    // methods. Also, flushBuffer() is useful when you don't want to actually
+    // flush the underlying output source
+    private final char buf[];     // capacity >= INT_MIN_VALUE_CHARS.length
+    private int len = 0;
+
+    JsonGeneratorImpl(Writer writer, BufferPool bufferPool) {
+        this.writer = writer;
+        this.bufferPool = bufferPool;
+        this.buf = bufferPool.take();
+    }
+
+    JsonGeneratorImpl(OutputStream out, BufferPool bufferPool) {
+        this(out, StandardCharsets.UTF_8, bufferPool);
+    }
+
+    JsonGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) {
+        this(new OutputStreamWriter(out, encoding), bufferPool);
+    }
+
+    @Override
+    public void flush() {
+        flushBuffer();
+        try {
+            writer.flush();
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_FLUSH_IO_ERR(), ioe);
+        }
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        if (currentContext.scope == Scope.IN_OBJECT) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (currentContext.scope == Scope.IN_NONE && !currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT());
+        }
+        writeComma();
+        writeChar('{');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_OBJECT);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeChar('{');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_OBJECT);
+        return this;
+    }
+
+    private JsonGenerator writeName(String name) {
+        writeComma();
+        writeEscapedString(name);
+        writeColon();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String fieldValue) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeEscapedString(fieldValue);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeInt(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN());
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString(value? "true" : "false");
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeString("null");
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        checkContextForValue();
+
+        switch (value.getValueType()) {
+            case ARRAY:
+                JsonArray array = (JsonArray)value;
+                writeStartArray();
+                for(JsonValue child: array) {
+                    write(child);
+                }
+                writeEnd();
+                break;
+            case OBJECT:
+                JsonObject object = (JsonObject)value;
+                writeStartObject();
+                for(Map.Entry<String, JsonValue> member: object.entrySet()) {
+                    write(member.getKey(), member.getValue());
+                }
+                writeEnd();
+                break;
+            case STRING:
+                JsonString str = (JsonString)value;
+                write(str.getString());
+                break;
+            case NUMBER:
+                JsonNumber number = (JsonNumber)value;
+                writeValue(number.toString());
+                popFieldContext();
+                break;
+            case TRUE:
+                write(true);
+                break;
+            case FALSE:
+                write(false);
+                break;
+            case NULL:
+                writeNull();
+                break;
+        }
+
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        if (currentContext.scope == Scope.IN_OBJECT) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        if (currentContext.scope == Scope.IN_NONE && !currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT());
+        }
+        writeComma();
+        writeChar('[');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_ARRAY);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        writeChar('[');
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_ARRAY);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        switch (value.getValueType()) {
+            case ARRAY:
+                JsonArray array = (JsonArray)value;
+                writeStartArray(name);
+                for(JsonValue child: array) {
+                    write(child);
+                }
+                writeEnd();
+                break;
+            case OBJECT:
+                JsonObject object = (JsonObject)value;
+                writeStartObject(name);
+                for(Map.Entry<String, JsonValue> member: object.entrySet()) {
+                    write(member.getKey(), member.getValue());
+                }
+                writeEnd();
+                break;
+            case STRING:
+                JsonString str = (JsonString)value;
+                write(name, str.getString());
+                break;
+            case NUMBER:
+                JsonNumber number = (JsonNumber)value;
+                writeValue(name, number.toString());
+                break;
+            case TRUE:
+                write(name, true);
+                break;
+            case FALSE:
+                write(name, false);
+                break;
+            case NULL:
+                writeNull(name);
+                break;
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        checkContextForValue();
+        writeComma();
+        writeEscapedString(value);
+        popFieldContext();
+        return this;
+    }
+
+
+    @Override
+    public JsonGenerator write(int value) {
+        checkContextForValue();
+        writeComma();
+        writeInt(value);
+        popFieldContext();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        checkContextForValue();
+        writeValue(String.valueOf(value));
+        popFieldContext();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        checkContextForValue();
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN());
+        }
+        writeValue(String.valueOf(value));
+        popFieldContext();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        checkContextForValue();
+        writeValue(value.toString());
+        popFieldContext();
+        return this;
+    }
+
+    private void checkContextForValue() {
+        if ((!currentContext.first && currentContext.scope != Scope.IN_ARRAY && currentContext.scope != Scope.IN_FIELD)
+                || (currentContext.first && currentContext.scope == Scope.IN_OBJECT)) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        checkContextForValue();
+        writeValue(value.toString());
+        popFieldContext();
+
+        return this;
+    }
+
+    private void popFieldContext() {
+        if (currentContext.scope == Scope.IN_FIELD) {
+            currentContext = stack.pop();
+        }
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        checkContextForValue();
+        writeComma();
+        writeString(value ? "true" : "false");
+        popFieldContext();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        checkContextForValue();
+        writeComma();
+        writeString("null");
+        popFieldContext();
+        return this;
+    }
+
+    private void writeValue(String value) {
+        writeComma();
+        writeString(value);
+    }
+
+    private void writeValue(String name, String value) {
+        writeComma();
+        writeEscapedString(name);
+        writeColon();
+        writeString(value);
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        if (currentContext.scope != Scope.IN_OBJECT) {
+            throw new JsonGenerationException(
+                    JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
+        }
+        writeName(name);
+        stack.push(currentContext);
+        currentContext = new Context(Scope.IN_FIELD);
+        currentContext.first = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        if (currentContext.scope == Scope.IN_NONE) {
+            throw new JsonGenerationException("writeEnd() cannot be called in no context");
+        }
+        writeChar(currentContext.scope == Scope.IN_ARRAY ? ']' : '}');
+        currentContext = stack.pop();
+        popFieldContext();
+        return this;
+    }
+
+    protected void writeComma() {
+        if (isCommaAllowed()) {
+            writeChar(',');
+        }
+        currentContext.first = false;
+    }
+
+    protected boolean inNone() {
+        return currentContext.scope == Scope.IN_NONE;
+    }
+
+    boolean isCommaAllowed() {
+        return !currentContext.first && currentContext.scope != Scope.IN_FIELD;
+    }
+
+    protected void writeColon() {
+        writeChar(':');
+    }
+
+    private static class Context {
+        boolean first = true;
+        final Scope scope;
+
+        Context(Scope scope) {
+            this.scope = scope;
+        }
+
+    }
+
+    @Override
+    public void close() {
+        if (currentContext.scope != Scope.IN_NONE || currentContext.first) {
+            throw new JsonGenerationException(JsonMessages.GENERATOR_INCOMPLETE_JSON());
+        }
+        flushBuffer();
+        try {
+            writer.close();
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_CLOSE_IO_ERR(), ioe);
+        }
+        bufferPool.recycle(buf);
+    }
+
+    // begin, end-1 indexes represent characters that need not
+    // be escaped
+    //
+    // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX
+    //    ^           ^                     ^             ^
+    //    |           |                     |             |
+    //   begin       end                   begin         end
+    void writeEscapedString(String string) {
+        writeChar('"');
+        int len = string.length();
+        for(int i = 0; i < len; i++) {
+            int begin = i, end = i;
+            char c = string.charAt(i);
+            // find all the characters that need not be escaped
+            // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF
+            while(c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) {
+                i++; end = i;
+                if (i < len) {
+                    c = string.charAt(i);
+                } else {
+                    break;
+                }
+            }
+            // Write characters without escaping
+            if (begin < end) {
+                writeString(string, begin, end);
+                if (i == len) {
+                    break;
+                }
+            }
+
+            switch (c) {
+                case '"':
+                case '\\':
+                    writeChar('\\'); writeChar(c);
+                    break;
+                case '\b':
+                    writeChar('\\'); writeChar('b');
+                    break;
+                case '\f':
+                    writeChar('\\'); writeChar('f');
+                    break;
+                case '\n':
+                    writeChar('\\'); writeChar('n');
+                    break;
+                case '\r':
+                    writeChar('\\'); writeChar('r');
+                    break;
+                case '\t':
+                    writeChar('\\'); writeChar('t');
+                    break;
+                default:
+                    String hex = "000" + Integer.toHexString(c);
+                    writeString("\\u" + hex.substring(hex.length() - 4));
+            }
+        }
+        writeChar('"');
+    }
+
+    void writeString(String str, int begin, int end) {
+        while (begin < end) {       // source begin and end indexes
+            int no = Math.min(buf.length - len, end - begin);
+            str.getChars(begin, begin + no, buf, len);
+            begin += no;            // Increment source index
+            len += no;              // Increment dest index
+            if (len >= buf.length) {
+                flushBuffer();
+            }
+        }
+    }
+
+    void writeString(String str) {
+        writeString(str, 0, str.length());
+    }
+
+    void writeChar(char c) {
+        if (len >= buf.length) {
+            flushBuffer();
+        }
+        buf[len++] = c;
+    }
+
+    // Not using Integer.toString() since it creates intermediary String
+    // Also, we want the chars to be copied to our buffer directly
+    void writeInt(int num) {
+        int size;
+        if (num == Integer.MIN_VALUE) {
+            size = INT_MIN_VALUE_CHARS.length;
+        } else {
+            size = (num < 0) ? stringSize(-num) + 1 : stringSize(num);
+        }
+        if (len+size >= buf.length) {
+            flushBuffer();
+        }
+        if (num == Integer.MIN_VALUE) {
+            System.arraycopy(INT_MIN_VALUE_CHARS, 0, buf, len, size);
+        } else {
+            fillIntChars(num, buf, len+size);
+        }
+        len += size;
+    }
+
+    // flushBuffer writes the buffered contents to writer. But incase of
+    // byte stream, an OuputStreamWriter is created and that buffers too.
+    // We may need to call OutputStreamWriter#flushBuffer() using
+    // reflection if that is really required (commented out below)
+    void flushBuffer() {
+        try {
+            if (len > 0) {
+                writer.write(buf, 0, len);
+                len = 0;
+            }
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.GENERATOR_WRITE_IO_ERR(), ioe);
+        }
+    }
+
+//    private static final Method flushBufferMethod;
+//    static {
+//        Method m = null;
+//        try {
+//            m = OutputStreamWriter.class.getDeclaredMethod("flushBuffer");
+//            m.setAccessible(true);
+//        } catch (Exception e) {
+//            // no-op
+//        }
+//        flushBufferMethod = m;
+//    }
+//    void flushBufferOSW() {
+//        flushBuffer();
+//        if (writer instanceof OutputStreamWriter) {
+//            try {
+//                flushBufferMethod.invoke(writer);
+//            } catch (Exception e) {
+//                // no-op
+//            }
+//        }
+//    }
+
+    // Requires positive x
+    private static int stringSize(int x) {
+        for (int i=0; ; i++)
+            if (x <= INT_CHARS_SIZE_TABLE[i])
+                return i+1;
+    }
+
+    /**
+     * Places characters representing the integer i into the
+     * character array buf. The characters are placed into
+     * the buffer backwards starting with the least significant
+     * digit at the specified index (exclusive), and working
+     * backwards from there.
+     *
+     * Will fail if i == Integer.MIN_VALUE
+     */
+    private static void fillIntChars(int i, char[] buf, int index) {
+        int q, r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Generate two digits per iteration
+        while (i >= 65536) {
+            q = i / 100;
+            // really: r = i - (q * 100);
+            r = i - ((q << 6) + (q << 5) + (q << 2));
+            i = q;
+            buf [--charPos] = DIGIT_ONES[r];
+            buf [--charPos] = DIGIT_TENS[r];
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i <= 65536, i);
+        for (;;) {
+            q = (i * 52429) >>> (16+3);
+            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
+            buf [--charPos] = DIGITS[r];
+            i = q;
+            if (i == 0) break;
+        }
+        if (sign != 0) {
+            buf [--charPos] = sign;
+        }
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java b/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java
new file mode 100644
index 0000000..f5ff424
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.stream.JsonLocation;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonLocationImpl implements JsonLocation {
+    static final JsonLocation UNKNOWN = new JsonLocationImpl(-1, -1, -1);
+
+    private final long columnNo;
+    private final long lineNo;
+    private final long offset;
+
+    JsonLocationImpl(long lineNo, long columnNo, long streamOffset) {
+        this.lineNo = lineNo;
+        this.columnNo = columnNo;
+        this.offset = streamOffset;
+    }
+
+    @Override
+    public long getLineNumber() {
+        return lineNo;
+    }
+
+    @Override
+    public long getColumnNumber() {
+        return columnNo;
+    }
+
+    @Override
+    public long getStreamOffset() {
+        return offset;
+    }
+
+    @Override
+    public String toString() {
+        return "(line no="+lineNo+", column no="+columnNo+", offset="+ offset +")";
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java b/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java
new file mode 100644
index 0000000..1a1a2e7
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonMergePatch;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+/**
+ * This class is an implementation of a JSON Merge Patch as specified in
+ * <a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>.
+ *
+ * @since 1.1
+ */
+
+public final class JsonMergePatchImpl implements JsonMergePatch {
+
+    private JsonValue patch;
+
+    public JsonMergePatchImpl(JsonValue patch) {
+        this.patch = patch;
+    }
+
+    @Override
+    public JsonValue apply(JsonValue target) {
+        return mergePatch(target, patch);
+    }
+
+    @Override
+    public JsonValue toJsonValue() {
+        return patch;
+    }
+    /**
+     * Applies the specified patch to the specified target.
+     * The target is not modified by the patch.
+     *
+     * @param target the {@code JsonValue} to apply the patch operations
+     * @param patch the patch
+     * @return the {@code JsonValue} as the result of applying the patch
+     *    operations on the target.
+     */
+    private static JsonValue mergePatch(JsonValue target, JsonValue patch) {
+
+        if (patch.getValueType() != JsonValue.ValueType.OBJECT) {
+            return patch;
+        }
+        if (target.getValueType() != JsonValue.ValueType.OBJECT) {
+            target = JsonValue.EMPTY_JSON_OBJECT;
+        }
+        JsonObject targetJsonObject = target.asJsonObject();
+        JsonObjectBuilder builder =
+            new JsonObjectBuilderImpl(targetJsonObject, JsonUtil.getInternalBufferPool());
+        patch.asJsonObject().forEach((key, value) -> {
+            if (value == JsonValue.NULL) {
+                if (targetJsonObject.containsKey(key)) {
+                    builder.remove(key);
+                }
+            } else if (targetJsonObject.containsKey(key)) {
+                builder.add(key, mergePatch(targetJsonObject.get(key), value));
+            } else {
+                builder.add(key, mergePatch(JsonValue.EMPTY_JSON_OBJECT, value));
+            }
+        });
+        return builder.build();
+    }
+
+    /**
+     * Generate a JSON Merge Patch from the source and target {@code JsonValue}.
+     * @param source the source
+     * @param target the target
+     * @return a JSON Patch which when applied to the source, yields the target
+     */
+    static JsonValue diff(JsonValue source, JsonValue target) {
+        if (source.getValueType() != JsonValue.ValueType.OBJECT ||
+                target.getValueType() != JsonValue.ValueType.OBJECT) {
+            return target;
+        }
+        JsonObject s = (JsonObject) source;
+        JsonObject t = (JsonObject) target;
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool());
+        // First find members to be replaced or removed
+        s.forEach((key, value) -> {
+            if (t.containsKey(key)) {
+                // key present in both.
+                if (! value.equals(t.get(key))) {
+                    // If the values are equal, nop, else get diff for the values
+                    builder.add(key, diff(value, t.get(key)));
+                }
+            } else {
+                builder.addNull(key);
+            }
+        });
+        // Then find members to be added
+        t.forEach((key, value) -> {
+            if (! s.containsKey(key))
+                builder.add(key, value);
+        });
+        return builder.build();
+    }
+
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonMessages.java b/impl/src/main/java/org/glassfish/json/JsonMessages.java
new file mode 100644
index 0000000..962cdbe
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonMessages.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+
+/**
+ * Defines string formatting method for each constant in the resource file
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonMessages {
+    private static final ResourceBundle BUNDLE =
+            ResourceBundle.getBundle("org.glassfish.json.messages");
+
+    // global/shared messages
+    static String INTERNAL_ERROR() {
+        return localize("internal.error");
+    }
+
+    // tokenizer messages
+    static String TOKENIZER_UNEXPECTED_CHAR(int unexpected, JsonLocation location) {
+        return localize("tokenizer.unexpected.char", unexpected, location);
+    }
+
+    static String TOKENIZER_EXPECTED_CHAR(int unexpected, JsonLocation location, char expected) {
+        return localize("tokenizer.expected.char", unexpected, location, expected);
+    }
+
+    static String TOKENIZER_IO_ERR() {
+        return localize("tokenizer.io.err");
+    }
+
+
+    // parser messages
+    static String PARSER_GETSTRING_ERR(JsonParser.Event event) {
+        return localize("parser.getString.err", event);
+    }
+
+    static String PARSER_ISINTEGRALNUMBER_ERR(JsonParser.Event event) {
+        return localize("parser.isIntegralNumber.err", event);
+    }
+
+    static String PARSER_GETINT_ERR(JsonParser.Event event) {
+        return localize("parser.getInt.err", event);
+    }
+
+    static String PARSER_GETLONG_ERR(JsonParser.Event event) {
+        return localize("parser.getLong.err", event);
+    }
+
+    static String PARSER_GETBIGDECIMAL_ERR(JsonParser.Event event) {
+        return localize("parser.getBigDecimal.err", event);
+    }
+
+    static String PARSER_GETARRAY_ERR(JsonParser.Event event) {
+        return localize("parser.getArray.err", event);
+    }
+
+    static String PARSER_GETOBJECT_ERR(JsonParser.Event event) {
+        return localize("parser.getObject.err", event);
+    }
+
+    static String PARSER_GETVALUE_ERR(JsonParser.Event event) {
+        return localize("parser.getValue.err", event);
+    }
+
+    static String PARSER_GETVALUESTREAM_ERR() {
+        return localize("parser.getValueStream.err");
+    }
+
+    static String PARSER_EXPECTED_EOF(JsonTokenizer.JsonToken token) {
+        return localize("parser.expected.eof", token);
+    }
+
+    static String PARSER_TOKENIZER_CLOSE_IO() {
+        return localize("parser.tokenizer.close.io");
+    }
+
+    static String PARSER_INVALID_TOKEN(JsonTokenizer.JsonToken token, JsonLocation location, String expectedTokens) {
+        return localize("parser.invalid.token", token, location, expectedTokens);
+    }
+
+    static String PARSER_STATE_ERR(JsonValue.ValueType type) {
+        return localize("parser.state.err", type);
+    }
+
+    static String PARSER_SCOPE_ERR(JsonValue value) {
+        return localize("parser.scope.err", value);
+    }
+
+    static String PARSER_INPUT_ENC_DETECT_FAILED() {
+        return localize("parser.input.enc.detect.failed");
+    }
+
+    static String PARSER_INPUT_ENC_DETECT_IOERR() {
+        return localize("parser.input.enc.detect.ioerr");
+    }
+    
+    static String DUPLICATE_KEY(String name) {
+        return localize("parser.duplicate.key", name);
+    }
+
+    // generator messages
+    static String GENERATOR_FLUSH_IO_ERR() {
+        return localize("generator.flush.io.err");
+    }
+
+    static String GENERATOR_CLOSE_IO_ERR() {
+        return localize("generator.close.io.err");
+    }
+
+    static String GENERATOR_WRITE_IO_ERR() {
+        return localize("generator.write.io.err");
+    }
+
+    static String GENERATOR_ILLEGAL_METHOD(Object scope) {
+        return localize("generator.illegal.method", scope);
+    }
+
+    static String GENERATOR_DOUBLE_INFINITE_NAN() {
+        return localize("generator.double.infinite.nan");
+    }
+
+    static String GENERATOR_INCOMPLETE_JSON() {
+        return localize("generator.incomplete.json");
+    }
+
+    static String GENERATOR_ILLEGAL_MULTIPLE_TEXT() {
+        return localize("generator.illegal.multiple.text");
+    }
+
+
+
+    // writer messages
+    static String WRITER_WRITE_ALREADY_CALLED() {
+        return localize("writer.write.already.called");
+    }
+
+    // reader messages
+    static String READER_READ_ALREADY_CALLED() {
+        return localize("reader.read.already.called");
+    }
+
+
+    // obj builder messages
+    static String OBJBUILDER_NAME_NULL() {
+        return localize("objbuilder.name.null");
+    }
+
+    static String OBJBUILDER_VALUE_NULL() {
+        return localize("objbuilder.value.null");
+    }
+
+    static String OBJBUILDER_OBJECT_BUILDER_NULL() {
+        return localize("objbuilder.object.builder.null");
+    }
+
+    static String OBJBUILDER_ARRAY_BUILDER_NULL() {
+        return localize("objbuilder.array.builder.null");
+    }
+
+
+    // array builder messages
+    static String ARRBUILDER_VALUE_NULL() {
+        return localize("arrbuilder.value.null");
+    }
+
+    static String ARRBUILDER_OBJECT_BUILDER_NULL() {
+        return localize("arrbuilder.object.builder.null");
+    }
+
+    static String ARRBUILDER_ARRAY_BUILDER_NULL() {
+        return localize("arrbuilder.array.builder.null");
+    }
+
+    static String ARRBUILDER_VALUELIST_NULL(int index, int size) {
+        return localize("arrbuilder.valuelist.null", index, size);
+    }
+
+    // json pointer messages
+    static String POINTER_FORMAT_INVALID() {
+        return localize("pointer.format.invalid");
+    }
+
+    static String POINTER_MAPPING_MISSING(JsonObject object, String key) {
+        return localize("pointer.mapping.missing", object, key);
+    }
+
+    static String POINTER_REFERENCE_INVALID(JsonValue.ValueType type) {
+        return localize("pointer.reference.invalid", type.name());
+    }
+
+    static String POINTER_ARRAY_INDEX_ERR(String token) {
+        return localize("pointer.array.index.err", token);
+    }
+
+    static String POINTER_ARRAY_INDEX_ILLEGAL(String token) {
+        return localize("pointer.array.index.illegal", token);
+    }
+
+    // nodereference messages
+    static String NODEREF_VALUE_ADD_ERR() {
+        return localize("noderef.value.add.err");
+    }
+
+    static String NODEREF_VALUE_CANNOT_REMOVE() {
+        return localize("noderef.value.cannot.remove");
+    }
+
+    static String NODEREF_OBJECT_MISSING(String key) {
+        return localize("noderef.object.missing", key);
+    }
+
+    static String NODEREF_ARRAY_INDEX_ERR(int index, int size) {
+        return localize("noderef.array.index.err", index, size);
+    }
+
+    // json patch messages
+    static String PATCH_MUST_BE_ARRAY() {
+        return localize("patch.must.be.array");
+    }
+
+    static String PATCH_MOVE_PROPER_PREFIX(String from, String path) {
+        return localize("patch.move.proper.prefix", from, path);
+    }
+
+    static String PATCH_MOVE_TARGET_NULL(String from) {
+        return localize("patch.move.target.null", from);
+    }
+
+    static String PATCH_TEST_FAILED(String path, String value) {
+        return localize("patch.test.failed", path, value);
+    }
+
+    static String PATCH_ILLEGAL_OPERATION(String operation) {
+        return localize("patch.illegal.operation", operation);
+    }
+
+    static String PATCH_MEMBER_MISSING(String operation, String member) {
+        return localize("patch.member.missing", operation, member);
+    }
+
+    static String PATCH_OPERATION_MISSING() {
+        return localize("patch.operation.missing");
+    }
+
+
+    private static String localize(String key, Object ... args) {
+        try {
+            String msg = BUNDLE.getString(key);
+            return MessageFormat.format(msg, args);
+        } catch (Exception e) {
+            return getDefaultMessage(key, args);
+        }
+    }
+
+    private static String getDefaultMessage(String key, Object ... args) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[failed to localize] ");
+        sb.append(key);
+        if (args != null) {
+            sb.append('(');
+            for (int i = 0; i < args.length; ++i) {
+                if (i != 0)
+                    sb.append(", ");
+                sb.append(String.valueOf(args[i]));
+            }
+            sb.append(')');
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java b/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java
new file mode 100644
index 0000000..6678270
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonNumber;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * JsonNumber impl. Subclasses provide optimized implementations
+ * when backed by int, long, BigDecimal
+ *
+ * @author Jitendra Kotamraju
+ */
+abstract class JsonNumberImpl implements JsonNumber {
+
+    private int hashCode;
+
+    static JsonNumber getJsonNumber(int num) {
+        return new JsonIntNumber(num);
+    }
+
+    static JsonNumber getJsonNumber(long num) {
+        return new JsonLongNumber(num);
+    }
+
+    static JsonNumber getJsonNumber(BigInteger value) {
+        return new JsonBigDecimalNumber(new BigDecimal(value));
+    }
+
+    static JsonNumber getJsonNumber(double value) {
+        //bigDecimal = new BigDecimal(value);
+        // This is the preferred way to convert double to BigDecimal
+        return new JsonBigDecimalNumber(BigDecimal.valueOf(value));
+    }
+
+    static JsonNumber getJsonNumber(BigDecimal value) {
+        return new JsonBigDecimalNumber(value);
+    }
+
+    // Optimized JsonNumber impl for int numbers.
+    private static final class JsonIntNumber extends JsonNumberImpl {
+        private final int num;
+        private BigDecimal bigDecimal;  // assigning it lazily on demand
+
+        JsonIntNumber(int num) {
+            this.num = num;
+        }
+
+        @Override
+        public boolean isIntegral() {
+            return true;
+        }
+
+        @Override
+        public int intValue() {
+            return num;
+        }
+
+        @Override
+        public int intValueExact() {
+            return num;
+        }
+
+        @Override
+        public long longValue() {
+            return num;
+        }
+
+        @Override
+        public long longValueExact() {
+            return num;
+        }
+
+        @Override
+        public double doubleValue() {
+            return num;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            // reference assignments are atomic. At the most some more temp
+            // BigDecimal objects are created
+            BigDecimal bd = bigDecimal;
+            if (bd == null) {
+                bigDecimal = bd = new BigDecimal(num);
+            }
+            return bd;
+        }
+
+        @Override
+        public Number numberValue() {
+            return num;
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toString(num);
+        }
+    }
+
+    // Optimized JsonNumber impl for long numbers.
+    private static final class JsonLongNumber extends JsonNumberImpl {
+        private final long num;
+        private BigDecimal bigDecimal;  // assigning it lazily on demand
+
+        JsonLongNumber(long num) {
+            this.num = num;
+        }
+
+        @Override
+        public boolean isIntegral() {
+            return true;
+        }
+
+        @Override
+        public int intValue() {
+            return (int) num;
+        }
+
+        @Override
+        public int intValueExact() {
+            return Math.toIntExact(num);
+        }
+
+        @Override
+        public long longValue() {
+            return num;
+        }
+
+        @Override
+        public long longValueExact() {
+            return num;
+        }
+
+        @Override
+        public double doubleValue() {
+            return num;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            // reference assignments are atomic. At the most some more temp
+            // BigDecimal objects are created
+            BigDecimal bd = bigDecimal;
+            if (bd == null) {
+                bigDecimal = bd = new BigDecimal(num);
+            }
+            return bd;
+        }
+
+        @Override
+        public Number numberValue() {
+            return num;
+        }
+
+        @Override
+        public String toString() {
+            return Long.toString(num);
+        }
+
+    }
+
+    // JsonNumber impl using BigDecimal numbers.
+    private static final class JsonBigDecimalNumber extends JsonNumberImpl {
+        private final BigDecimal bigDecimal;
+
+        JsonBigDecimalNumber(BigDecimal value) {
+            this.bigDecimal = value;
+        }
+
+        @Override
+        public BigDecimal bigDecimalValue() {
+            return bigDecimal;
+        }
+
+        @Override
+        public Number numberValue() {
+            return bigDecimalValue();
+        }
+
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return bigDecimalValue().scale() == 0;
+    }
+
+    @Override
+    public int intValue() {
+        return bigDecimalValue().intValue();
+    }
+
+    @Override
+    public int intValueExact() {
+        return bigDecimalValue().intValueExact();
+    }
+
+    @Override
+    public long longValue() {
+        return bigDecimalValue().longValue();
+    }
+
+    @Override
+    public long longValueExact() {
+        return bigDecimalValue().longValueExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return bigDecimalValue().doubleValue();
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return bigDecimalValue().toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return bigDecimalValue().toBigIntegerExact();
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == 0) {
+            hashCode = bigDecimalValue().hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj){
+            return true;
+        }
+        if (!(obj instanceof JsonNumber)) {
+            return false;
+        }
+        JsonNumber other = (JsonNumber)obj;
+        return bigDecimalValue().equals(other.bigDecimalValue());
+    }
+
+    @Override
+    public String toString() {
+        return bigDecimalValue().toString();
+    }
+
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java
new file mode 100644
index 0000000..1104055
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.*;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * JsonObjectBuilder implementation
+ *
+ * @author Jitendra Kotamraju
+ * @author Kin-man Chung
+ */
+class JsonObjectBuilderImpl implements JsonObjectBuilder {
+
+    protected Map<String, JsonValue> valueMap;
+    private final BufferPool bufferPool;
+    private final boolean rejectDuplicateKeys;
+
+    JsonObjectBuilderImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+        rejectDuplicateKeys = false;
+    }
+    
+    JsonObjectBuilderImpl(BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+    }
+
+    JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+        valueMap = new LinkedHashMap<>();
+        valueMap.putAll(object);
+        rejectDuplicateKeys = false;
+    }
+    
+    JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.bufferPool = bufferPool;
+        valueMap = new LinkedHashMap<>();
+        valueMap.putAll(object);
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+    }
+
+    JsonObjectBuilderImpl(Map<String, Object> map, BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+        valueMap = new LinkedHashMap<>();
+        populate(map);
+        rejectDuplicateKeys = false;
+    }
+    
+    JsonObjectBuilderImpl(Map<String, Object> map, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+    	this.bufferPool = bufferPool;
+    	valueMap = new LinkedHashMap<>();
+    	populate(map);
+    	this.rejectDuplicateKeys = rejectDuplicateKeys;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, JsonValue value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, String value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, BigInteger value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, BigDecimal value) {
+        validateName(name);
+        validateValue(value);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, int value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, long value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, double value) {
+        validateName(name);
+        putValueMap(name, JsonNumberImpl.getJsonNumber(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, boolean value) {
+        validateName(name);
+        putValueMap(name, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addNull(String name) {
+        validateName(name);
+        putValueMap(name, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, JsonObjectBuilder builder) {
+        validateName(name);
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_OBJECT_BUILDER_NULL());
+        }
+        putValueMap(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(String name, JsonArrayBuilder builder) {
+        validateName(name);
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_ARRAY_BUILDER_NULL());
+        }
+        putValueMap(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addAll(JsonObjectBuilder builder) {
+        if (builder == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_OBJECT_BUILDER_NULL());
+        }
+        if (valueMap == null) {
+            this.valueMap = new LinkedHashMap<>();
+        }
+        this.valueMap.putAll(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder remove(String name) {
+        validateName(name);
+        this.valueMap.remove(name);
+        return this;
+    }
+
+    @Override
+    public JsonObject build() {
+        Map<String, JsonValue> snapshot = (valueMap == null)
+                ? Collections.<String, JsonValue>emptyMap()
+                : Collections.unmodifiableMap(valueMap);
+        valueMap = null;
+        return new JsonObjectImpl(snapshot, bufferPool);
+    }
+
+    private void populate(Map<String, Object> map) {
+        final Set<String> fields = map.keySet();
+        for (String field : fields) {
+            Object value = map.get(field);
+            if (value != null && value instanceof Optional) {
+                ((Optional<?>) value).ifPresent(v ->
+                        this.valueMap.put(field, MapUtil.handle(v, bufferPool)));
+            } else {
+                this.valueMap.put(field, MapUtil.handle(value, bufferPool));
+            }
+        }
+    }
+
+    private void putValueMap(String name, JsonValue value) {
+        if (valueMap == null) {
+            this.valueMap = new LinkedHashMap<>();
+        }
+        JsonValue previousValue = valueMap.put(name, value);
+        if (rejectDuplicateKeys && previousValue != null) {
+            throw new IllegalStateException(JsonMessages.DUPLICATE_KEY(name));
+        }
+    }
+
+    private void validateName(String name) {
+        if (name == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_NAME_NULL());
+        }
+    }
+
+    private void validateValue(Object value) {
+        if (value == null) {
+            throw new NullPointerException(JsonMessages.OBJBUILDER_VALUE_NULL());
+        }
+    }
+
+    private static final class JsonObjectImpl extends AbstractMap<String, JsonValue> implements JsonObject {
+        private final Map<String, JsonValue> valueMap;      // unmodifiable
+        private final BufferPool bufferPool;
+        private int hashCode;
+
+        JsonObjectImpl(Map<String, JsonValue> valueMap, BufferPool bufferPool) {
+            this.valueMap = valueMap;
+            this.bufferPool = bufferPool;
+        }
+
+        @Override
+        public JsonArray getJsonArray(String name) {
+            return (JsonArray)get(name);
+        }
+
+        @Override
+        public JsonObject getJsonObject(String name) {
+            return (JsonObject)get(name);
+        }
+
+        @Override
+        public JsonNumber getJsonNumber(String name) {
+            return (JsonNumber)get(name);
+        }
+
+        @Override
+        public JsonString getJsonString(String name) {
+            return (JsonString)get(name);
+        }
+
+        @Override
+        public String getString(String name) {
+            return getJsonString(name).getString();
+        }
+
+        @Override
+        public String getString(String name, String defaultValue) {
+            JsonValue value = get(name);
+            if (value instanceof JsonString) {
+                return ((JsonString) value).getString();
+            } else {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public int getInt(String name) {
+            return getJsonNumber(name).intValue();
+        }
+
+        @Override
+        public int getInt(String name, int defaultValue) {
+            JsonValue value = get(name);
+            if (value instanceof JsonNumber) {
+                return ((JsonNumber) value).intValue();
+            } else {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String name) {
+            JsonValue value = get(name);
+            if (value == null) {
+                throw new NullPointerException();
+            } else if (value == JsonValue.TRUE) {
+                return true;
+            } else if (value == JsonValue.FALSE) {
+                return false;
+            } else {
+                throw new ClassCastException();
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String name, boolean defaultValue) {
+            JsonValue value = get(name);
+            if (value == JsonValue.TRUE) {
+                return true;
+            } else if (value == JsonValue.FALSE) {
+                return false;
+            } else {
+                return defaultValue;
+            }
+        }
+
+        @Override
+        public boolean isNull(String name) {
+            return get(name).equals(JsonValue.NULL);
+        }
+
+        @Override
+        public ValueType getValueType() {
+            return ValueType.OBJECT;
+        }
+
+        @Override
+        public Set<Entry<String, JsonValue>> entrySet() {
+            return valueMap.entrySet();
+        }
+
+        @Override
+        public int hashCode() {
+            if (hashCode == 0) {
+                hashCode = super.hashCode();
+            }
+            return hashCode;
+        }
+
+        @Override
+        public String toString() {
+            StringWriter sw = new StringWriter();
+            try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) {
+                jw.write(this);
+            }
+            return sw.toString();
+        }
+
+        @Override
+        public JsonObject asJsonObject() {
+            return this;
+        }
+
+        @Override
+        public int size() {
+            return valueMap.size();
+        }
+
+        @Override
+        public JsonValue get(Object key) {
+            return valueMap.get(key);
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return valueMap.containsKey(key);
+        }
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java
new file mode 100644
index 0000000..fb6203c
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.stream.JsonParserFactory;
+import jakarta.json.stream.JsonParser;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonParserFactoryImpl implements JsonParserFactory {
+    private final Map<String, ?> config = Collections.emptyMap();
+    private final BufferPool bufferPool;
+
+    JsonParserFactoryImpl(BufferPool bufferPool) {
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return new JsonParserImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return new JsonParserImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in, Charset charset) {
+        return new JsonParserImpl(in, charset, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(JsonArray array) {
+        return new JsonStructureParser(array);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+
+    @Override
+    public JsonParser createParser(JsonObject object) {
+        return new JsonStructureParser(object);
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonParserImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java
new file mode 100644
index 0000000..62bf4d4
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.util.AbstractMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import jakarta.json.stream.JsonParsingException;
+
+import org.glassfish.json.JsonTokenizer.JsonToken;
+import org.glassfish.json.api.BufferPool;
+
+/**
+ * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used
+ * to go to next parser state.
+ *
+ * @author Jitendra Kotamraju
+ * @author Kin-man Chung
+ */
+public class JsonParserImpl implements JsonParser {
+
+    private final BufferPool bufferPool;
+    private final boolean rejectDuplicateKeys;
+    private Context currentContext = new NoneContext();
+    private Event currentEvent;
+
+    private final Stack stack = new Stack();
+    private final JsonTokenizer tokenizer;
+    
+    public JsonParserImpl(Reader reader, BufferPool bufferPool) {
+        this(reader, bufferPool, false);
+    }
+
+    public JsonParserImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+        tokenizer = new JsonTokenizer(reader, bufferPool);
+    }
+
+    public JsonParserImpl(InputStream in, BufferPool bufferPool) {
+        this(in, bufferPool, false);
+    }
+
+    public JsonParserImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+        UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in);
+        tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool);
+    }
+
+    public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) {
+        this(in, encoding, bufferPool, false);
+    }
+
+    public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+        tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool);
+    }
+
+    @Override
+    public String getString() {
+        if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING
+                || currentEvent == Event.VALUE_NUMBER) {
+            return tokenizer.getValue();
+        }
+        throw new IllegalStateException(
+                JsonMessages.PARSER_GETSTRING_ERR(currentEvent));
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(currentEvent));
+        }
+        return tokenizer.isIntegral();
+    }
+
+    @Override
+    public int getInt() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETINT_ERR(currentEvent));
+        }
+        return tokenizer.getInt();
+    }
+
+    boolean isDefinitelyInt() {
+        return tokenizer.isDefinitelyInt();
+    }
+
+    boolean isDefinitelyLong() {
+        return tokenizer.isDefinitelyLong();
+    }
+
+    @Override
+    public long getLong() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETLONG_ERR(currentEvent));
+        }
+        return tokenizer.getLong();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException(
+                    JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent));
+        }
+        return tokenizer.getBigDecimal();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        if (currentEvent != Event.START_ARRAY) {
+            throw new IllegalStateException(
+                JsonMessages.PARSER_GETARRAY_ERR(currentEvent));
+        }
+        return getArray(new JsonArrayBuilderImpl(bufferPool));
+    }
+
+    @Override
+    public JsonObject getObject() {
+        if (currentEvent != Event.START_OBJECT) {
+            throw new IllegalStateException(
+                JsonMessages.PARSER_GETOBJECT_ERR(currentEvent));
+        }
+        return getObject(new JsonObjectBuilderImpl(bufferPool, rejectDuplicateKeys));
+    }
+
+    @Override
+    public JsonValue getValue() {
+        switch (currentEvent) {
+            case START_ARRAY:
+                return getArray(new JsonArrayBuilderImpl(bufferPool));
+            case START_OBJECT:
+                return getObject(new JsonObjectBuilderImpl(bufferPool));
+            case KEY_NAME:
+            case VALUE_STRING:
+                return new JsonStringImpl(getString());
+            case VALUE_NUMBER:
+                if (isDefinitelyInt()) {
+                    return JsonNumberImpl.getJsonNumber(getInt());
+                } else if (isDefinitelyLong()) {
+                    return JsonNumberImpl.getJsonNumber(getLong());
+                }
+                return JsonNumberImpl.getJsonNumber(getBigDecimal());
+            case VALUE_TRUE:
+                return JsonValue.TRUE;
+            case VALUE_FALSE:
+                return JsonValue.FALSE;
+            case VALUE_NULL:
+                return JsonValue.NULL;
+            case END_ARRAY:
+            case END_OBJECT:
+            default:
+                throw new IllegalStateException(JsonMessages.PARSER_GETVALUE_ERR(currentEvent));
+        }
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        if (currentEvent != Event.START_ARRAY) {
+            throw new IllegalStateException(
+                JsonMessages.PARSER_GETARRAY_ERR(currentEvent));
+        }
+        Spliterator<JsonValue> spliterator =
+                new Spliterators.AbstractSpliterator<JsonValue>(Long.MAX_VALUE, Spliterator.ORDERED) {
+            @Override
+            public Spliterator<JsonValue> trySplit() {
+                return null;
+            }
+            @Override
+            public boolean tryAdvance(Consumer<? super JsonValue> action) {
+                if (action == null) {
+                    throw new NullPointerException();
+                }
+                if (! hasNext()) {
+                    return false;
+                }
+                if (next() == JsonParser.Event.END_ARRAY) {
+                    return false;
+                }
+                action.accept(getValue());
+                return true;
+            }
+        };
+        return StreamSupport.stream(spliterator, false);
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        if (currentEvent != Event.START_OBJECT) {
+            throw new IllegalStateException(
+                JsonMessages.PARSER_GETOBJECT_ERR(currentEvent));
+        }
+        Spliterator<Map.Entry<String, JsonValue>> spliterator =
+                new Spliterators.AbstractSpliterator<Map.Entry<String, JsonValue>>(Long.MAX_VALUE, Spliterator.ORDERED) {
+            @Override
+            public Spliterator<Map.Entry<String,JsonValue>> trySplit() {
+                return null;
+            }
+            @Override
+            public boolean tryAdvance(Consumer<? super Map.Entry<String, JsonValue>> action) {
+                if (action == null) {
+                    throw new NullPointerException();
+                }
+                if (! hasNext()) {
+                    return false;
+                }
+                JsonParser.Event e = next();
+                if (e == JsonParser.Event.END_OBJECT) {
+                    return false;
+                }
+                if (e != JsonParser.Event.KEY_NAME) {
+                    throw new JsonException(JsonMessages.INTERNAL_ERROR());
+                }
+                String key = getString();
+                if (! hasNext()) {
+                    throw new JsonException(JsonMessages.INTERNAL_ERROR());
+                }
+                next();
+                JsonValue value = getValue();
+                action.accept(new AbstractMap.SimpleImmutableEntry<>(key, value));
+                return true;
+            }
+        };
+        return StreamSupport.stream(spliterator, false);
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        if (! (currentContext instanceof NoneContext)) {
+            throw new IllegalStateException(
+                JsonMessages.PARSER_GETVALUESTREAM_ERR());
+        }
+        Spliterator<JsonValue> spliterator =
+                new Spliterators.AbstractSpliterator<JsonValue>(Long.MAX_VALUE, Spliterator.ORDERED) {
+            @Override
+            public Spliterator<JsonValue> trySplit() {
+                return null;
+            }
+            @Override
+            public boolean tryAdvance(Consumer<? super JsonValue> action) {
+                if (action == null) {
+                    throw new NullPointerException();
+                }
+                if (! hasNext()) {
+                    return false;
+                }
+                next();
+                action.accept(getValue());
+                return true;
+            }
+        };
+        return StreamSupport.stream(spliterator, false);
+    }
+
+    @Override
+    public void skipArray() {
+        if (currentEvent == Event.START_ARRAY) {
+            currentContext.skip();
+            currentContext = stack.pop();
+            currentEvent = Event.END_ARRAY;
+        }
+    }
+
+    @Override
+    public void skipObject() {
+        if (currentEvent == Event.START_OBJECT) {
+            currentContext.skip();
+            currentContext = stack.pop();
+            currentEvent = Event.END_OBJECT;
+        }
+    }
+
+    private JsonArray getArray(JsonArrayBuilder builder) {
+        while(hasNext()) {
+            JsonParser.Event e = next();
+            if (e == JsonParser.Event.END_ARRAY) {
+                return builder.build();
+            }
+            builder.add(getValue());
+        }
+        throw parsingException(JsonToken.EOF, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL, SQUARECLOSE]");
+    }
+
+    private JsonObject getObject(JsonObjectBuilder builder) {
+        while(hasNext()) {
+            JsonParser.Event e = next();
+            if (e == JsonParser.Event.END_OBJECT) {
+                return builder.build();
+            }
+            String key = getString();
+            next();
+            builder.add(key, getValue());
+        }
+        throw parsingException(JsonToken.EOF, "[STRING, CURLYCLOSE]");
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return tokenizer.getLocation();
+    }
+
+    public JsonLocation getLastCharLocation() {
+        return tokenizer.getLastCharLocation();
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (stack.isEmpty() && (currentEvent != null && currentEvent.compareTo(Event.KEY_NAME) > 0)) {
+            JsonToken token = tokenizer.nextToken();
+            if (token != JsonToken.EOF) {
+                throw new JsonParsingException(JsonMessages.PARSER_EXPECTED_EOF(token),
+                        getLastCharLocation());
+            }
+            return false;
+        } else if (!stack.isEmpty() && !tokenizer.hasNextToken()) {
+            currentEvent = currentContext.getNextEvent();
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public Event next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+        return currentEvent = currentContext.getNextEvent();
+    }
+
+    @Override
+    public void close() {
+        try {
+            tokenizer.close();
+        } catch (IOException e) {
+            throw new JsonException(JsonMessages.PARSER_TOKENIZER_CLOSE_IO(), e);
+        }
+    }
+
+    // Using the optimized stack impl as we don't require other things
+    // like iterator etc.
+    private static final class Stack {
+        private Context head;
+
+        private void push(Context context) {
+            context.next = head;
+            head = context;
+        }
+
+        private Context pop() {
+            if (head == null) {
+                throw new NoSuchElementException();
+            }
+            Context temp = head;
+            head = head.next;
+            return temp;
+        }
+
+        private Context peek() {
+            return head;
+        }
+
+        private boolean isEmpty() {
+            return head == null;
+        }
+    }
+
+    private abstract class Context {
+        Context next;
+        abstract Event getNextEvent();
+        abstract void skip();
+    }
+
+    private final class NoneContext extends Context {
+        @Override
+        public Event getNextEvent() {
+            // Handle 1. {   2. [   3. value
+            JsonToken token = tokenizer.nextToken();
+            if (token == JsonToken.CURLYOPEN) {
+                stack.push(currentContext);
+                currentContext = new ObjectContext();
+                return Event.START_OBJECT;
+            } else if (token == JsonToken.SQUAREOPEN) {
+                stack.push(currentContext);
+                currentContext = new ArrayContext();
+                return Event.START_ARRAY;
+            } else if (token.isValue()) {
+                return token.getEvent();
+            }
+            throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+        }
+
+        @Override
+        void skip() {
+            // no-op
+        }
+    }
+
+    private JsonParsingException parsingException(JsonToken token, String expectedTokens) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+                JsonMessages.PARSER_INVALID_TOKEN(token, location, expectedTokens), location);
+    }
+
+    private final class ObjectContext extends Context {
+        private boolean firstValue = true;
+
+        /*
+         * Some more things could be optimized. For example, instead
+         * tokenizer.nextToken(), one could use tokenizer.matchColonToken() to
+         * match ':'. That might optimize a bit, but will fragment nextToken().
+         * I think the current one is more readable.
+         *
+         */
+        @Override
+        public Event getNextEvent() {
+            // Handle 1. }   2. name:value   3. ,name:value
+            JsonToken token = tokenizer.nextToken();
+            if (token == JsonToken.EOF) {
+                switch (currentEvent) {
+                    case START_OBJECT:
+                        throw parsingException(token, "[STRING, CURLYCLOSE]");
+                    case KEY_NAME:
+                        throw parsingException(token, "[COLON]");
+                    default:
+                        throw parsingException(token, "[COMMA, CURLYCLOSE]");
+                }
+            } else if (currentEvent == Event.KEY_NAME) {
+                // Handle 1. :value
+                if (token != JsonToken.COLON) {
+                    throw parsingException(token, "[COLON]");
+                }
+                token = tokenizer.nextToken();
+                if (token.isValue()) {
+                    return token.getEvent();
+                } else if (token == JsonToken.CURLYOPEN) {
+                    stack.push(currentContext);
+                    currentContext = new ObjectContext();
+                    return Event.START_OBJECT;
+                } else if (token == JsonToken.SQUAREOPEN) {
+                    stack.push(currentContext);
+                    currentContext = new ArrayContext();
+                    return Event.START_ARRAY;
+                }
+                throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+            } else {
+                // Handle 1. }   2. name   3. ,name
+                if (token == JsonToken.CURLYCLOSE) {
+                    currentContext = stack.pop();
+                    return Event.END_OBJECT;
+                }
+                if (firstValue) {
+                    firstValue = false;
+                } else {
+                    if (token != JsonToken.COMMA) {
+                        throw parsingException(token, "[COMMA]");
+                    }
+                    token = tokenizer.nextToken();
+                }
+                if (token == JsonToken.STRING) {
+                    return Event.KEY_NAME;
+                }
+                throw parsingException(token, "[STRING]");
+            }
+        }
+
+        @Override
+        void skip() {
+            JsonToken token;
+            int depth = 1;
+            do {
+                token = tokenizer.nextToken();
+                switch (token) {
+                    case CURLYCLOSE:
+                        depth--;
+                        break;
+                    case CURLYOPEN:
+                        depth++;
+                        break;
+                }
+            } while (!(token == JsonToken.CURLYCLOSE && depth == 0));
+        }
+
+    }
+
+    private final class ArrayContext extends Context {
+        private boolean firstValue = true;
+
+        // Handle 1. ]   2. value   3. ,value
+        @Override
+        public Event getNextEvent() {
+            JsonToken token = tokenizer.nextToken();
+            if (token == JsonToken.EOF) {
+                switch (currentEvent) {
+                    case START_ARRAY:
+                        throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+                    default:
+                        throw parsingException(token, "[COMMA, CURLYCLOSE]");
+                }
+            }
+            if (token == JsonToken.SQUARECLOSE) {
+                currentContext = stack.pop();
+                return Event.END_ARRAY;
+            }
+            if (firstValue) {
+                firstValue = false;
+            } else {
+                if (token != JsonToken.COMMA) {
+                    throw parsingException(token, "[COMMA]");
+                }
+                token = tokenizer.nextToken();
+            }
+            if (token.isValue()) {
+                return token.getEvent();
+            } else if (token == JsonToken.CURLYOPEN) {
+                stack.push(currentContext);
+                currentContext = new ObjectContext();
+                return Event.START_OBJECT;
+            } else if (token == JsonToken.SQUAREOPEN) {
+                stack.push(currentContext);
+                currentContext = new ArrayContext();
+                return Event.START_ARRAY;
+            }
+            throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
+        }
+
+        @Override
+        void skip() {
+            JsonToken token;
+            int depth = 1;
+            do {
+                token = tokenizer.nextToken();
+                switch (token) {
+                    case SQUARECLOSE:
+                        depth--;
+                        break;
+                    case SQUAREOPEN:
+                        depth++;
+                        break;
+                }
+            } while (!(token == JsonToken.SQUARECLOSE && depth == 0));
+        }
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java
new file mode 100644
index 0000000..78e3982
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonException;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatch.Operation;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+/**
+ * A builder for constructing a JSON Patch by adding
+ * JSON Patch operations incrementally.
+ * <p>
+ * The following illustrates the approach.
+ * <pre>
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonPatch patch = builder.add("/John/phones/office", "1234-567")
+ *                            .remove("/Amy/age")
+ *                            .build();
+ * </pre>
+ * The result is equivalent to the following JSON Patch.
+ * <pre>
+ * [
+ *    {"op" = "add", "path" = "/John/phones/office", "value" = "1234-567"},
+ *    {"op" = "remove", "path" = "/Amy/age"}
+ * ] </pre>
+ *
+ * @since 1.1
+ */
+public final class JsonPatchBuilderImpl implements JsonPatchBuilder {
+
+    private final JsonArrayBuilder builder;
+
+    /**
+     * Creates a JsonPatchBuilderImpl, starting with the specified
+     * JSON Patch
+     * @param patch the JSON Patch
+     */
+    public JsonPatchBuilderImpl(JsonArray patch) {
+        builder = new JsonArrayBuilderImpl(patch, JsonUtil.getInternalBufferPool());
+    }
+
+    /**
+     * Creates JsonPatchBuilderImpl with empty JSON Patch
+     */
+    public JsonPatchBuilderImpl() {
+        builder = new JsonArrayBuilderImpl(JsonUtil.getInternalBufferPool());
+    }
+
+    /**
+     * A convenience method for {@code new JsonPatchImpl(build()).apply(target)}.
+     * The target is not modified by the patch.
+     *
+     * @param <T> the target type, must be a subtype of {@link JsonStructure}
+     * @param target the target to apply the patch operations
+     * @return the transformed target after the patch
+     * @throws JsonException if the supplied JSON Patch is malformed or if
+     *    it contains references to non-existing members
+     */
+    public <T extends JsonStructure> T apply(T target) {
+        return build().apply(target);
+    }
+
+    /**
+     * Adds an "add" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder add(String path, JsonValue value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.ADD.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                   );
+        return this;
+    }
+
+    /**
+     * Adds an "add" JSON Patch operation
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder add(String path, String value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.ADD.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                   );
+        return this;
+    }
+
+    /**
+     * Adds an "add" JSON Patch operation
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder add(String path, int value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.ADD.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds an "add" JSON Patch operation
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder add(String path, boolean value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.ADD.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                   );
+        return this;
+    }
+
+    /**
+     * Adds a "remove" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder remove(String path) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.REMOVE.operationName())
+                           .add("path", path)
+                    );
+        return this;
+    }
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder replace(String path, JsonValue value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.REPLACE.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder replace(String path, String value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.REPLACE.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder replace(String path, int value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.REPLACE.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "replace" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder replace(String path, boolean value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.REPLACE.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "move" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param from the "from" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder move(String path, String from) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.MOVE.operationName())
+                           .add("path", path)
+                           .add("from", from)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "copy" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param from the "from" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder copy(String path, String from) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.COPY.operationName())
+                           .add("path", path)
+                           .add("from", from)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder test(String path, JsonValue value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.TEST.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder test(String path, String value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.TEST.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder test(String path, int value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.TEST.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Adds a "test" JSON Patch operation.
+     * @param path the "path" member of the operation
+     * @param value the "value" member of the operation
+     * @return this JsonPatchBuilder
+     */
+    @Override
+    public JsonPatchBuilder test(String path, boolean value) {
+        builder.add(new JsonObjectBuilderImpl(JsonUtil.getInternalBufferPool())
+                           .add("op", Operation.TEST.operationName())
+                           .add("path", path)
+                           .add("value", value)
+                  );
+        return this;
+    }
+
+    /**
+     * Returns the patch operations in a JsonArray
+     * @return the patch operations in a JsonArray
+     */
+    public JsonArray buildAsJsonArray() {
+        return builder.build();
+    }
+
+    /**
+     * Returns the patch operation in a JsonPatch
+     * @return the patch operation in a JsonPatch
+     */
+    @Override
+    public JsonPatch build() {
+        return new JsonPatchImpl(buildAsJsonArray());
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java b/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java
new file mode 100644
index 0000000..1f9352d
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatch.Operation;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonString;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonValue.ValueType;
+
+/**
+ * This class is an immutable representation of a JSON Patch as specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>.
+ * <p>A {@code JsonPatch} can be instantiated with {@link Json#createPatch(JsonArray)}
+ * by specifying the patch operations in a JSON Patch. Alternately, it
+ * can also be constructed with a {@link JsonPatchBuilder}.
+ * </p>
+ * The following illustrates both approaches.
+ * <p>1. Construct a JsonPatch with a JSON Patch.
+ * <pre>{@code
+ *   JsonArray contacts = ... // The target to be patched
+ *   JsonArray patch = ...  ; // JSON Patch
+ *   JsonPatch jsonpatch = Json.createPatch(patch);
+ *   JsonArray result = jsonpatch.apply(contacts);
+ * } </pre>
+ * 2. Construct a JsonPatch with JsonPatchBuilder.
+ * <pre>{@code
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonArray result = builder.add("/John/phones/office", "1234-567")
+ *                             .remove("/Amy/age")
+ *                             .build()
+ *                             .apply(contacts);
+ * } </pre>
+ *
+ * @since 1.1
+ */
+public class JsonPatchImpl implements JsonPatch {
+
+    private final JsonArray patch;
+
+    /**
+     * Constructs a JsonPatchImpl
+     * @param patch the JSON Patch
+     */
+    public JsonPatchImpl(JsonArray patch) {
+        this.patch = patch;
+    }
+
+    /**
+     * Compares this {@code JsonPatchImpl} with another object.
+     * @param obj the object to compare this {@code JsonPatchImpl} against
+     * @return true if the given object is a {@code JsonPatchImpl} with the same
+     * reference tokens as this one, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null || obj.getClass() != JsonPatchImpl.class)
+            return false;
+        return patch.equals(((JsonPatchImpl)obj).patch);
+    }
+
+    /**
+     * Returns the hash code value for this {@code JsonPatchImpl}.
+     *
+     * @return the hash code value for this {@code JsonPatchImpl} object
+     */
+    @Override
+    public int hashCode() {
+        return patch.hashCode();
+    }
+
+    /**
+     * Returns the JSON Patch text
+     * @return the JSON Patch text
+     */
+    @Override
+    public String toString() {
+        return patch.toString();
+    }
+
+    /**
+     * Applies the patch operations to the specified {@code target}.
+     * The target is not modified by the patch.
+     *
+     * @param target the target to apply the patch operations
+     * @return the transformed target after the patch
+     * @throws JsonException if the supplied JSON Patch is malformed or if
+     *    it contains references to non-existing members
+     */
+    @Override
+    public JsonStructure apply(JsonStructure target) {
+
+        JsonStructure result = target;
+
+        for (JsonValue operation: patch) {
+            if (operation.getValueType() != ValueType.OBJECT) {
+                throw new JsonException(JsonMessages.PATCH_MUST_BE_ARRAY());
+            }
+            result = apply(result, (JsonObject) operation);
+        }
+        return result;
+    }
+
+    @Override
+    public JsonArray toJsonArray() {
+        return patch;
+    }
+
+    /**
+     * Generates a JSON Patch from the source and target {@code JsonStructure}.
+     * The generated JSON Patch need not be unique.
+     * @param source the source
+     * @param target the target, must be the same type as the source
+     * @return a JSON Patch which when applied to the source, yields the target
+     */
+    public static JsonArray diff(JsonStructure source, JsonStructure target) {
+        return (new DiffGenerator()).diff(source, target);
+    }
+
+    /**
+     * Applies a JSON Patch operation to the target.
+     * @param target the target to apply the operation
+     * @param operation the JSON Patch operation
+     * @return the target after the patch
+     */
+    private JsonStructure apply(JsonStructure target, JsonObject operation) {
+
+        JsonPointer pointer = getPointer(operation, "path");
+        JsonPointer from;
+        JsonString op = operation.getJsonString("op");
+        if (op == null) {
+            throw new JsonException(JsonMessages.PATCH_OPERATION_MISSING());
+        }
+        switch (Operation.fromOperationName(op.getString())) {
+            case ADD:
+                return pointer.add(target, getValue(operation));
+            case REPLACE:
+                return pointer.replace(target, getValue(operation));
+            case REMOVE:
+                return pointer.remove(target);
+            case COPY:
+                from = getPointer(operation, "from");
+                return pointer.add(target, from.getValue(target));
+            case MOVE:
+                // Check if from is a proper prefix of path
+                String dest = operation.getString("path");
+                String src = operation.getString("from");
+                if (dest.startsWith(src) && src.length() < dest.length()) {
+                    throw new JsonException(JsonMessages.PATCH_MOVE_PROPER_PREFIX(src, dest));
+                }
+                from = getPointer(operation, "from");
+                // Check if 'from' exists in target object
+                if (!from.containsValue(target)) {
+                    throw new JsonException(JsonMessages.PATCH_MOVE_TARGET_NULL(src));
+                }
+                if (pointer.equals(from)) {
+                    // nop
+                    return target;
+                }
+                return pointer.add(from.remove(target), from.getValue(target));
+            case TEST:
+                if (! getValue(operation).equals(pointer.getValue(target))) {
+                    throw new JsonException(JsonMessages.PATCH_TEST_FAILED(operation.getString("path"), getValue(operation).toString()));
+                }
+                return target;
+            default:
+                throw new JsonException(JsonMessages.PATCH_ILLEGAL_OPERATION(operation.getString("op")));
+        }
+    }
+
+    private JsonPointer getPointer(JsonObject operation, String member) {
+        JsonString pointerString = operation.getJsonString(member);
+        if (pointerString == null) {
+            missingMember(operation.getString("op"), member);
+        }
+        return new JsonPointerImpl(pointerString.getString());
+    }
+
+    private JsonValue getValue(JsonObject operation) {
+        JsonValue value = operation.get("value");
+        if (value == null) {
+            missingMember(operation.getString("op"), "value");
+        }
+        return value;
+    }
+
+    private void missingMember(String op, String  member) {
+        throw new JsonException(JsonMessages.PATCH_MEMBER_MISSING(op, member));
+    }
+
+    static class DiffGenerator {
+        private JsonPatchBuilder builder;
+
+        JsonArray diff(JsonStructure source, JsonStructure target) {
+            builder = new JsonPatchBuilderImpl();
+            diff("", source, target);
+            return builder.build().toJsonArray();
+        }
+
+        private void diff(String path, JsonValue source, JsonValue target) {
+            if (source.equals(target)) {
+                return;
+            }
+            ValueType s = source.getValueType();
+            ValueType t = target.getValueType();
+            if (s == ValueType.OBJECT && t == ValueType.OBJECT) {
+                diffObject(path, (JsonObject) source, (JsonObject) target);
+            } else if (s == ValueType.ARRAY && t == ValueType.ARRAY) {
+                diffArray(path, (JsonArray) source, (JsonArray) target);
+            } else {
+                builder.replace(path, target);
+            }
+        }
+
+        private void diffObject(String path, JsonObject source, JsonObject target) {
+            source.forEach((key, value) -> {
+                if (target.containsKey(key)) {
+                    diff(path + '/' + Json.encodePointer(key), value, target.get(key));
+                } else {
+                    builder.remove(path + '/' + Json.encodePointer(key));
+                }
+            });
+            target.forEach((key, value) -> {
+                if (! source.containsKey(key)) {
+                    builder.add(path + '/' + Json.encodePointer(key), value);
+                }
+            });
+        }
+
+        /*
+         * For array element diff, find the longest common subsequence, per
+         * http://en.wikipedia.org/wiki/Longest_common_subsequence_problem .
+         * We modify the algorithm to generate a replace if possible.
+         */
+        private void diffArray(String path, JsonArray source, JsonArray target) {
+            /* The array c keeps track of length of the subsequence. To avoid
+             * computing the equality of array elements again, we
+             * left shift its value by 1, and use the low order bit to mark
+             * that two items are equal.
+             */
+            int m = source.size();
+            int n = target.size();
+            int [][] c = new int[m+1][n+1];
+            for (int i = 0; i < m+1; i++)
+                c[i][0] = 0;
+            for (int i = 0; i < n+1; i++)
+                c[0][i] = 0;
+            for (int i = 0; i < m; i++) {
+                for (int j = 0; j < n; j++) {
+                    if (source.get(i).equals(target.get(j))) {
+                        c[i+1][j+1] = ((c[i][j]) & ~1) + 3;
+                        // 3 = (1 << 1) | 1;
+                    } else {
+                        c[i+1][j+1] = Math.max(c[i+1][j], c[i][j+1]) & ~1;
+                    }
+                }
+            }
+
+            emit(path, source, target, c, m, n);
+        }
+
+        private void emit(final String path,
+                          final JsonArray source,
+                          final JsonArray target,
+                          final int[][] c,
+                          final int i,
+                          final int j) {
+           if (i == 0) {
+               if (j > 0) {
+                   emit(path, source, target, c, i, j - 1);
+                   builder.add(path + '/' + (j - 1), target.get(j - 1));
+               }
+           } else if (j == 0) {
+               if (i > 0) {
+                   builder.remove(path + '/' + (i - 1));
+                   emit(path, source, target, c, i - 1, j);
+               }
+           } else if ((c[i][j] & 1) == 1) {
+               emit(path, source, target, c, i - 1, j - 1);
+           } else {
+               final int f = c[i][j-1] >> 1;
+               final int g = c[i-1][j] >> 1;
+               if (f > g) {
+                   emit(path, source, target, c, i, j - 1);
+                   builder.add(path + '/' + (j - 1), target.get(j - 1));
+               } else if (f < g) {
+                   builder.remove(path + '/' + (i - 1));
+                   emit(path, source, target, c, i - 1, j);
+               } else { // f == g) {
+                   diff(path + '/' + (i - 1), source.get(i - 1),
+                     target.get(j - 1));
+                   emit(path, source, target, c, i - 1, j - 1);
+               }
+           }
+        }
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java b/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java
new file mode 100644
index 0000000..b91a576
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import java.io.Serializable;
+import java.util.function.BiFunction;
+
+/**
+ * <p>This class is an immutable representation of a JSON Pointer as specified in
+ * <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+ * </p>
+ * <p> A JSON Pointer, when applied to a target {@link JsonValue},
+ * defines a reference location in the target.</p>
+ * <p> An empty JSON Pointer string defines a reference to the target itself.</p>
+ * <p> If the JSON Pointer string is non-empty, it must be a sequence
+ * of '/' prefixed tokens, and the target must either be a {@link JsonArray}
+ * or {@link JsonObject}. If the target is a {@code JsonArray}, the pointer
+ * defines a reference to an array element, and the last token specifies the index.
+ * If the target is a {@link JsonObject}, the pointer defines a reference to a
+ * name/value pair, and the last token specifies the name.
+ * </p>
+ * <p> The method {@link #getValue getValue()} returns the referenced value.
+ * The methods {@link #add add()}, {@link #replace replace()},
+ * and {@link #remove remove()} executes the operations specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>. </p>
+ *
+ * @since 1.1
+ */
+
+public final class JsonPointerImpl implements JsonPointer, Serializable {
+
+    private static final long serialVersionUID = -8123110179640843141L;
+    private final String[] tokens;
+    private final String jsonPointer;
+
+    /**
+     * Constructs and initializes a JsonPointerImpl.
+     * @param jsonPointer the JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer
+     */
+    public JsonPointerImpl(String jsonPointer) {
+        this.jsonPointer = jsonPointer;
+        tokens = jsonPointer.split("/", -1);  // keep the trailing blanks
+        if (! "".equals(tokens[0])) {
+            throw new JsonException(JsonMessages.POINTER_FORMAT_INVALID());
+        }
+        for (int i = 1; i < tokens.length; i++) {
+            String token = tokens[i];
+            StringBuilder reftoken = new StringBuilder();
+            for (int j = 0; j < token.length(); j++) {
+                char ch = token.charAt(j);
+                if (ch == '~' && j < token.length() - 1) {
+                    char ch1 = token.charAt(j+1);
+                    if (ch1 == '0') {
+                        ch = '~'; j++;
+                    } else if (ch1 == '1') {
+                        ch = '/'; j++;
+                    }
+                }
+                reftoken.append(ch);
+            }
+            tokens[i] = reftoken.toString();
+        }
+    }
+
+    /**
+     * Compares this {@code JsonPointer} with another object.
+     * @param obj the object to compare this {@code JsonPointer} against
+     * @return true if the given object is a {@code JsonPointer} with the same
+     * reference tokens as this one, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null || obj.getClass() != JsonPointerImpl.class)
+            return false;
+        return jsonPointer.equals(((JsonPointerImpl)obj).jsonPointer);
+    }
+
+    /**
+     * Returns the hash code value for this {@code JsonPointer} object.
+     * The hash code of this object is defined by the hash codes of it's reference tokens.
+     *
+     * @return the hash code value for this {@code JsonPointer} object
+     */
+    @Override
+    public int hashCode() {
+        return jsonPointer.hashCode();
+    }
+
+    /**
+     * Returns {@code true} if there is a value at the referenced location in the specified {@code target}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return {@code true} if this pointer points to a value in a specified {@code target}.
+     */
+    @Override
+    public boolean containsValue(JsonStructure target) {
+        NodeReference[] refs = getReferences(target);
+        return refs[0].contains();
+    }
+
+    /**
+     * Returns the value at the referenced location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the referenced value in the target.
+     * @throws NullPointerException if {@code target} is null
+     * @throws JsonException if the referenced value does not exist
+     */
+    @Override
+    public JsonValue getValue(JsonStructure target) {
+        NodeReference[] refs = getReferences(target);
+        return refs[0].get();
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     * <ol>
+     * <li>If the reference is the target (empty JSON Pointer string),
+     * the specified {@code value}, which must be the same type as
+     * specified {@code target}, is returned.</li>
+     * <li>If the reference is an array element, the specified {@code value} is inserted
+     * into the array, at the referenced index. The value currently at that location, and
+     * any subsequent values, are shifted to the right (adds one to the indices).
+     * Index starts with 0. If the reference is specified with a "-", or if the
+     * index is equal to the size of the array, the value is appended to the array.</li>
+     * <li>If the reference is a name/value pair of a {@code JsonObject}, and the
+     * referenced value exists, the value is replaced by the specified {@code value}.
+     * If the value does not exist, a new name/value pair is added to the object.</li>
+     * </ol>
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the reference is an array element and
+     * the index is out of range ({@code index < 0 || index > array size}),
+     * or if the pointer contains references to non-existing objects or arrays.
+     */
+    @Override
+    public JsonStructure add(JsonStructure target, JsonValue value) {
+        return execute(NodeReference::add, target, value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the referenced value does not exist,
+     *    or if the reference is the target.
+     */
+    @Override
+    public JsonStructure replace(JsonStructure target, JsonValue value) {
+        return execute(NodeReference::replace, target, value);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException if the referenced value does not exist,
+     *    or if the reference is the target.
+     */
+    @Override
+    public JsonStructure remove(JsonStructure target) {
+        return execute((r,v)->r.remove(), target, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return jsonPointer;
+    }
+
+    /**
+     * Executes the operation
+     * @param op a {code BiFunction} used to specify the operation to execute on
+     *    the leaf node of the Json Pointer
+     * @param target the target JsonStructure for this JsonPointer
+     * @param value the JsonValue for add and replace, can be null for getvalue and remove
+     */
+    private JsonStructure execute(BiFunction<NodeReference, JsonValue, JsonStructure> op,
+            JsonStructure target, JsonValue value) {
+
+        NodeReference[] refs = getReferences(target);
+        JsonStructure result = op.apply(refs[0], value);
+        for (int i = 1; i < refs.length; i++) {
+            result = refs[i].replace(result);
+        }
+        return result;
+    }
+
+    /**
+     * Computes the {@code NodeReference}s for each node on the path of
+     * the JSON Pointer, in reverse order, starting from the leaf node
+     */
+    private NodeReference[] getReferences(JsonStructure target) {
+        NodeReference[] references;
+        // First check if this is a reference to a JSON value tree
+        if (tokens.length == 1) {
+            references = new NodeReference[1];
+            references[0] = NodeReference.of(target);
+            return references;
+        }
+
+        references = new NodeReference[tokens.length-1];
+        JsonValue value = target;
+        int s = tokens.length;
+        for (int i = 1; i < s; i++) {
+             // Start with index 1, skipping the "" token
+            switch (value.getValueType()) {
+                case OBJECT:
+                    JsonObject object = (JsonObject) value;
+                    references[s-i-1] = NodeReference.of(object, tokens[i]);
+                    if (i < s-1) {
+                        value = object.get(tokens[i]);
+                        if (value == null) {
+                            // Except for the last name, the mapping must exist
+                            throw new JsonException(JsonMessages.POINTER_MAPPING_MISSING(object, tokens[i]));
+                        }
+                    }
+                    break;
+                case ARRAY:
+                    int index = getIndex(tokens[i]);
+                    JsonArray array = (JsonArray) value;
+                    references[s-i-1] = NodeReference.of(array, index);
+                    if (i < s-1 && index != -1) {
+                        if (index >= array.size()) {
+                            throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size()));
+                        }
+                        // The last array index in the path can have index value of -1
+                        // ("-" in the JSON pointer)
+                        value = array.get(index);
+                    }
+                    break;
+                default:
+                    throw new JsonException(JsonMessages.POINTER_REFERENCE_INVALID(value.getValueType()));
+             }
+        }
+        return references;
+    }
+
+    /**
+     * Computes the array index
+     * @param token the input string token
+     * @return the array index. -1 if the token is "-"
+     * @throws JsonException if the string token is not in correct format
+     */
+    static private int getIndex(String token) {
+        if (token == null || token.length() == 0) {
+            throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ERR(token));
+        }
+        if (token.equals("-")) {
+            return -1;
+        }
+        if (token.equals("0")) {
+            return 0;
+        }
+        if (token.charAt(0) == '+' || token.charAt(0) == '-') {
+            throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ERR(token));
+        }
+        try {
+            return Integer.parseInt(token);
+        } catch (NumberFormatException ex) {
+            throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ILLEGAL(token), ex);
+       }
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java
new file mode 100644
index 0000000..f1c28a2
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.stream.JsonGenerator;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonPrettyGeneratorImpl extends JsonGeneratorImpl {
+    private int indentLevel;
+    private static final String INDENT = "    ";
+
+    public JsonPrettyGeneratorImpl(Writer writer, BufferPool bufferPool) {
+        super(writer, bufferPool);
+    }
+
+    public JsonPrettyGeneratorImpl(OutputStream out, BufferPool bufferPool) {
+        super(out, bufferPool);
+    }
+
+    public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) {
+        super(out, encoding, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        super.writeStartObject();
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        super.writeStartObject(name);
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        super.writeStartArray();
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        super.writeStartArray(name);
+        indentLevel++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        writeNewLine();
+        indentLevel--;
+        writeIndent();
+        super.writeEnd();
+        return this;
+    }
+
+    private void writeIndent() {
+        for(int i=0; i < indentLevel; i++) {
+            writeString(INDENT);
+        }
+    }
+
+    @Override
+    protected void writeComma() {
+        super.writeComma();
+        if (isCommaAllowed() && !inNone()) {
+            writeChar('\n');
+            writeIndent();
+        }
+    }
+
+    @Override
+    protected void writeColon() {
+        super.writeColon();
+        writeChar(' ');
+    }
+
+    private void writeNewLine() {
+        writeChar('\n');
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java
new file mode 100644
index 0000000..a4ebc8a
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+import org.glassfish.json.api.JsonConfig;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+import jakarta.json.spi.JsonProvider;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Jitendra Kotamraju
+ * @author Kin-man Chung
+ * @author Alex Soto
+ */
+public class JsonProviderImpl extends JsonProvider {
+    private final BufferPool bufferPool = new BufferPoolImpl();
+
+    @Override
+    public JsonGenerator createGenerator(Writer writer) {
+        return new JsonGeneratorImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(OutputStream out) {
+        return new JsonGeneratorImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(Reader reader) {
+        return new JsonParserImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonParser createParser(InputStream in) {
+        return new JsonParserImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(Map<String, ?> config) {
+        BufferPool pool = null;
+        if (config != null && config.containsKey(BufferPool.class.getName())) {
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+        }
+        if (pool == null) {
+            pool = bufferPool;
+        }
+        return new JsonParserFactoryImpl(pool);
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
+        Map<String, Object> providerConfig;
+        boolean prettyPrinting;
+        BufferPool pool;
+        if (config == null) {
+            providerConfig = Collections.emptyMap();
+            prettyPrinting = false;
+            pool = bufferPool;
+        } else {
+            providerConfig = new HashMap<>();
+            if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) {
+                providerConfig.put(JsonGenerator.PRETTY_PRINTING, true);
+            }
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+            if (pool != null) {
+                providerConfig.put(BufferPool.class.getName(), pool);
+            } else {
+                pool = bufferPool;
+            }
+            providerConfig = Collections.unmodifiableMap(providerConfig);
+        }
+
+        return new JsonGeneratorFactoryImpl(providerConfig, prettyPrinting, pool);
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return new JsonReaderImpl(reader, bufferPool);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return new JsonReaderImpl(in, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return new JsonWriterImpl(writer, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return new JsonWriterImpl(out, bufferPool);
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+        Map<String, Object> providerConfig;
+        boolean prettyPrinting;
+        BufferPool pool;
+        if (config == null) {
+            providerConfig = Collections.emptyMap();
+            prettyPrinting = false;
+            pool = bufferPool;
+        } else {
+            providerConfig = new HashMap<>();
+            if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) {
+                providerConfig.put(JsonGenerator.PRETTY_PRINTING, true);
+            }
+            pool = (BufferPool)config.get(BufferPool.class.getName());
+            if (pool != null) {
+                providerConfig.put(BufferPool.class.getName(), pool);
+            } else {
+                pool = bufferPool;
+            }
+            providerConfig = Collections.unmodifiableMap(providerConfig);
+        }
+        return new JsonWriterFactoryImpl(providerConfig, prettyPrinting, pool);
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+        Map<String, Object> providerConfig;
+        boolean rejectDuplicateKeys;
+        BufferPool pool;
+        if (config == null) {
+            providerConfig = Collections.emptyMap();
+            rejectDuplicateKeys = false;
+            pool = bufferPool;
+        } else {
+            providerConfig = new HashMap<>();
+            if (rejectDuplicateKeys = JsonProviderImpl.isRejectDuplicateKeysEnabled(config)) {
+                providerConfig.put(JsonConfig.REJECT_DUPLICATE_KEYS, true);
+            }
+            pool = (BufferPool) config.get(BufferPool.class.getName());
+            if (pool != null) {
+                providerConfig.put(BufferPool.class.getName(), pool);
+            } else {
+                pool = bufferPool;
+            }
+            providerConfig = Collections.unmodifiableMap(providerConfig);
+        }
+        return new JsonReaderFactoryImpl(providerConfig, pool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject object) {
+        return new JsonObjectBuilderImpl(object, bufferPool);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        return new JsonObjectBuilderImpl(map, bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl(bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray array) {
+        return new JsonArrayBuilderImpl(array, bufferPool);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> collection) {
+        return new JsonArrayBuilderImpl(collection, bufferPool);
+    }
+
+    @Override
+    public JsonPointer createPointer(String jsonPointer) {
+        return new JsonPointerImpl(jsonPointer);
+    }
+
+    @Override
+    public JsonPatchBuilder createPatchBuilder() {
+        return new JsonPatchBuilderImpl();
+    }
+
+    @Override
+    public JsonPatchBuilder createPatchBuilder(JsonArray array) {
+        return new JsonPatchBuilderImpl(array);
+    }
+
+    @Override
+    public JsonPatch createPatch(JsonArray array) {
+        return new JsonPatchImpl(array);
+    }
+
+    @Override
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        return new JsonPatchImpl(JsonPatchImpl.diff(source, target));
+    }
+
+    @Override
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        return new JsonMergePatchImpl(patch);
+    }
+
+    @Override
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        return new JsonMergePatchImpl(JsonMergePatchImpl.diff(source, target));
+    }
+
+    @Override
+    public JsonString createValue(String value) {
+        return new JsonStringImpl(value);
+    }
+
+    @Override
+    public JsonNumber createValue(int value) {
+        return JsonNumberImpl.getJsonNumber(value);
+    }
+
+    @Override
+    public JsonNumber createValue(long value) {
+        return JsonNumberImpl.getJsonNumber(value);
+    }
+
+    @Override
+    public JsonNumber createValue(double value) {
+        return JsonNumberImpl.getJsonNumber(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigInteger value) {
+        return JsonNumberImpl.getJsonNumber(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigDecimal value) {
+        return JsonNumberImpl.getJsonNumber(value);
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
+    	BufferPool pool = bufferPool;
+    	boolean rejectDuplicateKeys = false;
+    	if (config != null) {
+    		if (config.containsKey(BufferPool.class.getName())) {
+    			pool = (BufferPool) config.get(BufferPool.class.getName());
+    		}
+    		rejectDuplicateKeys = JsonProviderImpl.isRejectDuplicateKeysEnabled(config);
+    	}
+        return new JsonBuilderFactoryImpl(pool, rejectDuplicateKeys);
+    }
+
+    static boolean isPrettyPrintingEnabled(Map<String, ?> config) {
+        return config.containsKey(JsonGenerator.PRETTY_PRINTING);
+    }
+
+    static boolean isRejectDuplicateKeysEnabled(Map<String, ?> config) {
+        return config.containsKey(JsonConfig.REJECT_DUPLICATE_KEYS);
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java
new file mode 100644
index 0000000..df98252
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonReaderFactoryImpl implements JsonReaderFactory {
+    private final Map<String, ?> config;
+    private final BufferPool bufferPool;
+    private final boolean rejectDuplicateKeys;
+
+    JsonReaderFactoryImpl(Map<String, ?> config, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        this.config = config;
+        this.bufferPool = bufferPool;
+        this.rejectDuplicateKeys = rejectDuplicateKeys;
+    }
+
+    @Override
+    public JsonReader createReader(Reader reader) {
+        return new JsonReaderImpl(reader, bufferPool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in) {
+        return new JsonReaderImpl(in, bufferPool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public JsonReader createReader(InputStream in, Charset charset) {
+        return new JsonReaderImpl(in, charset, bufferPool, rejectDuplicateKeys);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java
new file mode 100644
index 0000000..967d705
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParsingException;
+
+/**
+ * JsonReader impl using parser and builders.
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonReaderImpl implements JsonReader {
+    private final JsonParserImpl parser;
+    private boolean readDone;
+    private final BufferPool bufferPool;
+    
+    JsonReaderImpl(Reader reader, BufferPool bufferPool) {
+        this(reader, bufferPool, false);
+    }
+
+    JsonReaderImpl(Reader reader, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        parser = new JsonParserImpl(reader, bufferPool, rejectDuplicateKeys);
+        this.bufferPool = bufferPool;
+    }
+
+    JsonReaderImpl(InputStream in, BufferPool bufferPool) {
+        this(in, bufferPool, false);
+    }
+
+    JsonReaderImpl(InputStream in, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        parser = new JsonParserImpl(in, bufferPool, rejectDuplicateKeys);
+        this.bufferPool = bufferPool;
+    }
+
+    JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) {
+        this(in, charset, bufferPool, false);
+    }
+
+    JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool, boolean rejectDuplicateKeys) {
+        parser = new JsonParserImpl(in, charset, bufferPool, rejectDuplicateKeys);
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonStructure read() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            try {
+                JsonParser.Event e = parser.next();
+                if (e == JsonParser.Event.START_ARRAY) {
+                    return parser.getArray();
+                } else if (e == JsonParser.Event.START_OBJECT) {
+                    return parser.getObject();
+                }
+            } catch (IllegalStateException ise) {
+                throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation());
+            }
+        }
+        throw new JsonException(JsonMessages.INTERNAL_ERROR());
+    }
+
+    @Override
+    public JsonObject readObject() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            try {
+                parser.next();
+                return parser.getObject();
+            } catch (IllegalStateException ise) {
+                throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation());
+            }
+        }
+        throw new JsonException(JsonMessages.INTERNAL_ERROR());
+    }
+
+    @Override
+    public JsonArray readArray() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            try {
+                parser.next();
+                return parser.getArray();
+            } catch (IllegalStateException ise) {
+                throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation());
+            }
+        }
+        throw new JsonException(JsonMessages.INTERNAL_ERROR());
+    }
+
+    @Override
+    public JsonValue readValue() {
+        if (readDone) {
+            throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED());
+        }
+        readDone = true;
+        if (parser.hasNext()) {
+            try {
+                parser.next();
+                return parser.getValue();
+            } catch (IllegalStateException ise) {
+                throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation());
+            }
+        }
+        throw new JsonException(JsonMessages.INTERNAL_ERROR());
+    }
+
+    @Override
+    public void close() {
+        readDone = true;
+        parser.close();
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonStringImpl.java b/impl/src/main/java/org/glassfish/json/JsonStringImpl.java
new file mode 100644
index 0000000..ca9fb4f
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonStringImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonString;
+
+/**
+ * JsonString impl
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonStringImpl implements JsonString {
+
+    private final String value;
+
+    JsonStringImpl(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String getString() {
+        return value;
+    }
+
+    @Override
+    public CharSequence getChars() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.STRING;
+    }
+
+    @Override
+    public int hashCode() {
+        return getString().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj){
+            return true;
+        }
+        if (!(obj instanceof JsonString)) {
+            return false;
+        }
+        JsonString other = (JsonString)obj;
+        return getString().equals(other.getString());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append('"');
+
+        for(int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF
+            if (c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) {
+                sb.append(c);
+            } else {
+                switch (c) {
+                    case '"':
+                    case '\\':
+                        sb.append('\\'); sb.append(c);
+                        break;
+                    case '\b':
+                        sb.append('\\'); sb.append('b');
+                        break;
+                    case '\f':
+                        sb.append('\\'); sb.append('f');
+                        break;
+                    case '\n':
+                        sb.append('\\'); sb.append('n');
+                        break;
+                    case '\r':
+                        sb.append('\\'); sb.append('r');
+                        break;
+                    case '\t':
+                        sb.append('\\'); sb.append('t');
+                        break;
+                    default:
+                        String hex = "000" + Integer.toHexString(c);
+                        sb.append("\\u").append(hex.substring(hex.length() - 4));
+                }
+            }
+        }
+
+        sb.append('"');
+        return sb.toString();
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonStructureParser.java b/impl/src/main/java/org/glassfish/json/JsonStructureParser.java
new file mode 100644
index 0000000..e57dafa
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonStructureParser.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import java.math.BigDecimal;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * {@link JsonParser} implementation on top of JsonArray/JsonObject
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonStructureParser implements JsonParser {
+
+    private Scope current;
+    private Event state;
+    private final Deque<Scope> scopeStack = new ArrayDeque<>();
+
+    JsonStructureParser(JsonArray array) {
+        current = new ArrayScope(array);
+    }
+
+    JsonStructureParser(JsonObject object) {
+        current = new ObjectScope(object);
+    }
+
+    @Override
+    public String getString() {
+        switch (state) {
+            case KEY_NAME:
+                return ((ObjectScope)current).key;
+            case VALUE_STRING:
+                return ((JsonString)current.getJsonValue()).getString();
+            case VALUE_NUMBER:
+                return ((JsonNumber)current.getJsonValue()).toString();
+            default:
+                throw new IllegalStateException(JsonMessages.PARSER_GETSTRING_ERR(state));
+        }
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).isIntegral();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(state));
+    }
+
+    @Override
+    public int getInt() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).intValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETINT_ERR(state));
+    }
+
+    @Override
+    public long getLong() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).longValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETLONG_ERR(state));
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (state == Event.VALUE_NUMBER) {
+            return ((JsonNumber)current.getJsonValue()).bigDecimalValue();
+        }
+        throw new IllegalStateException(JsonMessages.PARSER_GETBIGDECIMAL_ERR(state));
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return JsonLocationImpl.UNKNOWN;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return !((state == Event.END_OBJECT || state == Event.END_ARRAY) && scopeStack.isEmpty());
+    }
+
+    @Override
+    public Event next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+        transition();
+        return state;
+    }
+
+    private void transition() {
+        if (state == null) {
+            state = current instanceof ArrayScope ? Event.START_ARRAY : Event.START_OBJECT;
+        } else {
+            if (state == Event.END_OBJECT || state == Event.END_ARRAY) {
+                current = scopeStack.pop();
+            }
+            if (current instanceof ArrayScope) {
+                if (current.hasNext()) {
+                    current.next();
+                    state = getState(current.getJsonValue());
+                    if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
+                        scopeStack.push(current);
+                        current = Scope.createScope(current.getJsonValue());
+                    }
+                } else {
+                    state = Event.END_ARRAY;
+                }
+            } else {
+                // ObjectScope
+                if (state == Event.KEY_NAME) {
+                    state = getState(current.getJsonValue());
+                    if (state == Event.START_ARRAY || state == Event.START_OBJECT) {
+                        scopeStack.push(current);
+                        current = Scope.createScope(current.getJsonValue());
+                    }
+                } else {
+                    if (current.hasNext()) {
+                        current.next();
+                        state = Event.KEY_NAME;
+                    } else {
+                        state = Event.END_OBJECT;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public void skipObject() {
+        if (current instanceof ObjectScope) {
+            int depth = 1;
+            do {
+                if (state == Event.KEY_NAME) {
+                    state = getState(current.getJsonValue());
+                    switch (state) {
+                        case START_OBJECT:
+                            depth++;
+                            break;
+                        case END_OBJECT:
+                            depth--;
+                            break;
+                        default:
+                            //no-op
+                    }
+                } else {
+                    if (current.hasNext()) {
+                        current.next();
+                        state = Event.KEY_NAME;
+                    } else {
+                        state = Event.END_OBJECT;
+                        depth--;
+                    }
+                }
+            } while (state != Event.END_OBJECT && depth > 0);
+        }
+    }
+
+    @Override
+    public void skipArray() {
+        if (current instanceof ArrayScope) {
+            int depth = 1;
+            do {
+                if (current.hasNext()) {
+                    current.next();
+                    state = getState(current.getJsonValue());
+                    switch (state) {
+                        case START_ARRAY:
+                            depth++;
+                            break;
+                        case END_ARRAY:
+                            depth--;
+                            break;
+                        default:
+                            //no-op
+                    }
+                } else {
+                    state = Event.END_ARRAY;
+                    depth--;
+                }
+            } while (!(state == Event.END_ARRAY && depth == 0));
+        }
+    }
+
+    private static Event getState(JsonValue value) {
+        switch (value.getValueType()) {
+            case ARRAY:
+                return Event.START_ARRAY;
+            case OBJECT:
+                return Event.START_OBJECT;
+            case STRING:
+                return Event.VALUE_STRING;
+            case NUMBER:
+                return Event.VALUE_NUMBER;
+            case TRUE:
+                return Event.VALUE_TRUE;
+            case FALSE:
+                return Event.VALUE_FALSE;
+            case NULL:
+                return Event.VALUE_NULL;
+            default:
+                throw new JsonException(JsonMessages.PARSER_STATE_ERR(value.getValueType()));
+        }
+    }
+
+    private static abstract class Scope implements Iterator {
+        abstract JsonValue getJsonValue();
+
+        static Scope createScope(JsonValue value) {
+            if (value instanceof JsonArray) {
+                return new ArrayScope((JsonArray)value);
+            } else if (value instanceof JsonObject) {
+                return new ObjectScope((JsonObject)value);
+            }
+            throw new JsonException(JsonMessages.PARSER_SCOPE_ERR(value));
+        }
+    }
+
+    private static class ArrayScope extends Scope {
+        private final Iterator<JsonValue> it;
+        private JsonValue value;
+
+        ArrayScope(JsonArray array) {
+            this.it = array.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public JsonValue next() {
+            value = it.next();
+            return value;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        JsonValue getJsonValue() {
+            return value;
+        }
+
+    }
+
+    private static class ObjectScope extends Scope {
+        private final Iterator<Map.Entry<String, JsonValue>> it;
+        private JsonValue value;
+        private String key;
+
+        ObjectScope(JsonObject object) {
+            this.it = object.entrySet().iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public Map.Entry<String, JsonValue> next() {
+            Map.Entry<String, JsonValue> next = it.next();
+            this.key = next.getKey();
+            this.value = next.getValue();
+            return next;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        JsonValue getJsonValue() {
+            return value;
+        }
+
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonTokenizer.java b/impl/src/main/java/org/glassfish/json/JsonTokenizer.java
new file mode 100644
index 0000000..be2b952
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonTokenizer.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonException;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParsingException;
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+import jakarta.json.stream.JsonParser.Event;
+
+/**
+ * JSON Tokenizer
+ *
+ * @author Jitendra Kotamraju
+ */
+final class JsonTokenizer implements Closeable {
+    // Table to look up hex ch -> value (for e.g HEX['F'] = 15, HEX['5'] = 5)
+    private final static int[] HEX = new int[128];
+    static {
+        Arrays.fill(HEX, -1);
+        for (int i='0'; i <= '9'; i++) {
+            HEX[i] = i-'0';
+        }
+        for (int i='A'; i <= 'F'; i++) {
+            HEX[i] = 10+i-'A';
+        }
+        for (int i='a'; i <= 'f'; i++) {
+            HEX[i] = 10+i-'a';
+        }
+    }
+    private final static int HEX_LENGTH = HEX.length;
+
+    private final BufferPool bufferPool;
+
+    private final Reader reader;
+
+    // Internal buffer that is used for parsing. It is also used
+    // for storing current string and number value token
+    private char[] buf;
+
+    // Indexes in buffer
+    //
+    // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX
+    //    ^           ^                     ^             ^
+    //    |           |                     |             |
+    //   storeBegin  storeEnd            readBegin      readEnd
+    private int readBegin;
+    private int readEnd;
+    private int storeBegin;
+    private int storeEnd;
+
+    // line number of the current pointer of parsing char
+    private long lineNo = 1;
+
+    // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+    // ^
+    // |
+    // bufferOffset
+    //
+    // offset of the last \r\n or \n. will be used to calculate column number
+    // of a token or an error. This may be outside of the buffer.
+    private long lastLineOffset = 0;
+    // offset in the stream for the start of the buffer, will be used in
+    // calculating JsonLocation's stream offset, column no.
+    private long bufferOffset = 0;
+
+    private boolean minus;
+    private boolean fracOrExp;
+    private BigDecimal bd;
+
+    enum JsonToken {
+        CURLYOPEN(Event.START_OBJECT, false),
+        SQUAREOPEN(Event.START_ARRAY, false),
+        COLON(null, false),
+        COMMA(null, false),
+        STRING(Event.VALUE_STRING, true),
+        NUMBER(Event.VALUE_NUMBER, true),
+        TRUE(Event.VALUE_TRUE, true),
+        FALSE(Event.VALUE_FALSE, true),
+        NULL(Event.VALUE_NULL, true),
+        CURLYCLOSE(Event.END_OBJECT, false),
+        SQUARECLOSE(Event.END_ARRAY, false),
+        EOF(null, false);
+
+        private final JsonParser.Event event;
+        private final boolean value;
+
+        JsonToken(JsonParser.Event event, boolean value) {
+            this.event = event;
+            this.value = value;
+        }
+
+        JsonParser.Event getEvent() {
+            return event;
+        }
+
+        boolean isValue() {
+            return value;
+        }
+    }
+
+    JsonTokenizer(Reader reader, BufferPool bufferPool) {
+        this.reader = reader;
+        this.bufferPool = bufferPool;
+        buf = bufferPool.take();
+    }
+
+    private void readString() {
+        // when inPlace is true, no need to copy chars
+        boolean inPlace = true;
+        storeBegin = storeEnd = readBegin;
+
+        do {
+            // Write unescaped char block within the current buffer
+            if (inPlace) {
+                int ch;
+                while(readBegin < readEnd && ((ch=buf[readBegin]) >= 0x20) && ch != '\\') {
+                    if (ch == '"') {
+                        storeEnd = readBegin++; // ++ to consume quote char
+                        return;                 // Got the entire string
+                    }
+                    readBegin++;                // consume unescaped char
+                }
+                storeEnd = readBegin;
+            }
+
+            // string may be crossing buffer boundaries and may contain
+            // escaped characters.
+            int ch = read();
+            if (ch >= 0x20 && ch != 0x22 && ch != 0x5c) {
+                if (!inPlace) {
+                    buf[storeEnd] = (char)ch;
+                }
+                storeEnd++;
+                continue;
+            }
+            switch (ch) {
+                case '\\':
+                    inPlace = false;        // Now onwards need to copy chars
+                    unescape();
+                    break;
+                case '"':
+                    return;
+                default:
+                    throw unexpectedChar(ch);
+            }
+        } while (true);
+    }
+
+    private void unescape() {
+        int ch = read();
+        switch (ch) {
+            case 'b':
+                buf[storeEnd++] = '\b';
+                break;
+            case 't':
+                buf[storeEnd++] = '\t';
+                break;
+            case 'n':
+                buf[storeEnd++] = '\n';
+                break;
+            case 'f':
+                buf[storeEnd++] = '\f';
+                break;
+            case 'r':
+                buf[storeEnd++] = '\r';
+                break;
+            case '"':
+            case '\\':
+            case '/':
+                buf[storeEnd++] = (char)ch;
+                break;
+            case 'u': {
+                int unicode = 0;
+                for (int i = 0; i < 4; i++) {
+                    int ch3 = read();
+                    int digit = (ch3 >= 0 && ch3 < HEX_LENGTH) ? HEX[ch3] : -1;
+                    if (digit < 0) {
+                        throw unexpectedChar(ch3);
+                    }
+                    unicode = (unicode << 4)|digit;
+                }
+                buf[storeEnd++] = (char)unicode;
+                break;
+            }
+            default:
+                throw unexpectedChar(ch);
+        }
+    }
+
+    // Reads a number char. If the char is within the buffer, directly
+    // reads from the buffer. Otherwise, uses read() which takes care
+    // of resizing, filling up the buf, adjusting the pointers
+    private int readNumberChar() {
+        if (readBegin < readEnd) {
+            return buf[readBegin++];
+        } else {
+            storeEnd = readBegin;
+            return read();
+        }
+    }
+
+    private void readNumber(int ch)  {
+        storeBegin = storeEnd = readBegin-1;
+        // sign
+        if (ch == '-') {
+            this.minus = true;
+            ch = readNumberChar();
+            if (ch < '0' || ch >'9') {
+                throw unexpectedChar(ch);
+            }
+        }
+
+        // int
+        if (ch == '0') {
+            ch = readNumberChar();
+        } else {
+            do {
+                ch = readNumberChar();
+            } while (ch >= '0' && ch <= '9');
+        }
+
+        // frac
+        if (ch == '.') {
+            this.fracOrExp = true;
+            int count = 0;
+            do {
+                ch = readNumberChar();
+                count++;
+            } while (ch >= '0' && ch <= '9');
+            if (count == 1) {
+                throw unexpectedChar(ch);
+            }
+        }
+
+        // exp
+        if (ch == 'e' || ch == 'E') {
+            this.fracOrExp = true;
+            ch = readNumberChar();
+            if (ch == '+' || ch == '-') {
+                ch = readNumberChar();
+            }
+            int count;
+            for (count = 0; ch >= '0' && ch <= '9'; count++) {
+                ch = readNumberChar();
+            }
+            if (count == 0) {
+                throw unexpectedChar(ch);
+            }
+        }
+        if (ch != -1) {
+            // Only reset readBegin if eof has not been reached
+            readBegin--;
+            storeEnd = readBegin;
+        }
+    }
+
+    private void readTrue() {
+        int ch1 = read();
+        if (ch1 != 'r') {
+            throw expectedChar(ch1, 'r');
+        }
+        int ch2 = read();
+        if (ch2 != 'u') {
+            throw expectedChar(ch2, 'u');
+        }
+        int ch3 = read();
+        if (ch3 != 'e') {
+            throw expectedChar(ch3, 'e');
+        }
+    }
+
+    private void readFalse() {
+        int ch1 = read();
+        if (ch1 != 'a') {
+            throw expectedChar(ch1, 'a');
+        }
+        int ch2 = read();
+        if (ch2 != 'l') {
+            throw expectedChar(ch2, 'l');
+        }
+        int ch3 = read();
+        if (ch3 != 's') {
+            throw expectedChar(ch3, 's');
+        }
+        int ch4 = read();
+        if (ch4 != 'e') {
+            throw expectedChar(ch4, 'e');
+        }
+    }
+
+    private void readNull() {
+        int ch1 = read();
+        if (ch1 != 'u') {
+            throw expectedChar(ch1, 'u');
+        }
+        int ch2 = read();
+        if (ch2 != 'l') {
+            throw expectedChar(ch2, 'l');
+        }
+        int ch3 = read();
+        if (ch3 != 'l') {
+            throw expectedChar(ch3, 'l');
+        }
+    }
+
+    /*
+     * Could be optimized if the parser uses separate methods to match colon
+     * etc (that would avoid the switch statement cost in certain cases)
+     */
+    JsonToken nextToken() {
+        reset();
+        int ch = read();
+
+        // whitespace
+        while (ch == 0x20 || ch == 0x09 || ch == 0x0a || ch == 0x0d) {
+            if (ch == '\r') {
+                ++lineNo;
+                ch = read();
+                if (ch == '\n') {
+                    lastLineOffset = bufferOffset+readBegin;
+                } else {
+                    lastLineOffset = bufferOffset+readBegin-1;
+                    continue;
+                }
+            } else if (ch == '\n') {
+                ++lineNo;
+                lastLineOffset = bufferOffset+readBegin;
+            }
+            ch = read();
+        }
+
+        switch (ch) {
+            case '"':
+                readString();
+                return JsonToken.STRING;
+            case '{':
+                return JsonToken.CURLYOPEN;
+            case '[':
+                return JsonToken.SQUAREOPEN;
+            case ':':
+                return JsonToken.COLON;
+            case ',':
+                return JsonToken.COMMA;
+            case 't':
+                readTrue();
+                return JsonToken.TRUE;
+            case 'f':
+                readFalse();
+                return JsonToken.FALSE;
+            case 'n':
+                readNull();
+                return JsonToken.NULL;
+            case ']':
+                return JsonToken.SQUARECLOSE;
+            case '}':
+                return JsonToken.CURLYCLOSE;
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '-':
+                readNumber(ch);
+                return JsonToken.NUMBER;
+            case -1:
+                return JsonToken.EOF;
+            default:
+                throw unexpectedChar(ch);
+        }
+    }
+
+    boolean hasNextToken() {
+        reset();
+        int ch = peek();
+
+        // whitespace
+        while (ch == 0x20 || ch == 0x09 || ch == 0x0a || ch == 0x0d) {
+            if (ch == '\r') {
+                ++lineNo;
+                ++readBegin;
+                ch = peek();
+                if (ch == '\n') {
+                    lastLineOffset = bufferOffset+readBegin+1;
+                } else {
+                    lastLineOffset = bufferOffset+readBegin;
+                    continue;
+                }
+            } else if (ch == '\n') {
+                ++lineNo;
+                lastLineOffset = bufferOffset+readBegin+1;
+            }
+            ++readBegin;
+            ch = peek();
+        }
+        return ch != -1;
+    }
+
+    private int peek() {
+        try {
+            if (readBegin == readEnd) {     // need to fill the buffer
+                int len = fillBuf();
+                if (len == -1) {
+                    return -1;
+                }
+                assert len != 0;
+                readBegin = storeEnd;
+                readEnd = readBegin+len;
+            }
+            return buf[readBegin];
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.TOKENIZER_IO_ERR(), ioe);
+        }
+    }
+
+    // Gives the location of the last char. Used for
+    // JsonParsingException.getLocation
+    JsonLocation getLastCharLocation() {
+        // Already read the char, so subtracting -1
+        return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset, bufferOffset +readBegin-1);
+    }
+
+    // Gives the parser location. Used for JsonParser.getLocation
+    JsonLocation getLocation() {
+        return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset+1, bufferOffset +readBegin);
+    }
+
+    private int read() {
+        try {
+            if (readBegin == readEnd) {     // need to fill the buffer
+                int len = fillBuf();
+                if (len == -1) {
+                    return -1;
+                }
+                assert len != 0;
+                readBegin = storeEnd;
+                readEnd = readBegin+len;
+            }
+            return buf[readBegin++];
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.TOKENIZER_IO_ERR(), ioe);
+        }
+    }
+
+    private int fillBuf() throws IOException {
+        if (storeEnd != 0) {
+            int storeLen = storeEnd-storeBegin;
+            if (storeLen > 0) {
+                // there is some store data
+                if (storeLen == buf.length) {
+                    // buffer is full, double the capacity
+                    char[] doubleBuf = Arrays.copyOf(buf, 2 * buf.length);
+                    bufferPool.recycle(buf);
+                    buf = doubleBuf;
+                } else {
+                    // Left shift all the stored data to make space
+                    System.arraycopy(buf, storeBegin, buf, 0, storeLen);
+                    storeEnd = storeLen;
+                    storeBegin = 0;
+                    bufferOffset += readBegin-storeEnd;
+                }
+            } else {
+                storeBegin = storeEnd = 0;
+                bufferOffset += readBegin;
+            }
+        } else {
+            bufferOffset += readBegin;
+        }
+        // Fill the rest of the buf
+        return reader.read(buf, storeEnd, buf.length-storeEnd);
+    }
+
+    // state associated with the current token is no more valid
+    private void reset() {
+        if (storeEnd != 0) {
+            storeBegin = 0;
+            storeEnd = 0;
+            bd = null;
+            minus = false;
+            fracOrExp = false;
+        }
+    }
+
+    String getValue() {
+        return new String(buf, storeBegin, storeEnd-storeBegin);
+    }
+
+    BigDecimal getBigDecimal() {
+        if (bd == null) {
+            bd = new BigDecimal(buf, storeBegin, storeEnd-storeBegin);
+        }
+        return bd;
+    }
+
+    int getInt() {
+        // no need to create BigDecimal for common integer values (1-9 digits)
+        int storeLen = storeEnd-storeBegin;
+        if (!fracOrExp && (storeLen <= 9 || (minus && storeLen <= 10))) {
+            int num = 0;
+            int i = minus ? 1 : 0;
+            for(; i < storeLen; i++) {
+                num = num * 10 + (buf[storeBegin+i] - '0');
+            }
+            return minus ? -num : num;
+        } else {
+            return getBigDecimal().intValue();
+        }
+    }
+    
+    long getLong() {
+        // no need to create BigDecimal for common integer values (1-18 digits)
+        int storeLen = storeEnd-storeBegin;
+        if (!fracOrExp && (storeLen <= 18 || (minus && storeLen <= 19))) {
+            long num = 0;
+            int i = minus ? 1 : 0;
+            for(; i < storeLen; i++) {
+                num = num * 10 + (buf[storeBegin+i] - '0');
+            }
+            return minus ? -num : num;
+        } else {
+            return getBigDecimal().longValue();
+        }
+    }
+
+    // returns true for common integer values (1-9 digits).
+    // So there are cases it will return false even though the number is int
+    boolean isDefinitelyInt() {
+        int storeLen = storeEnd-storeBegin;
+        return !fracOrExp && (storeLen <= 9 || (minus && storeLen <= 10));
+    }
+    
+    // returns true for common long values (1-18 digits).
+    // So there are cases it will return false even though the number is long
+    boolean isDefinitelyLong() {
+    	int storeLen = storeEnd-storeBegin;
+    	return !fracOrExp && (storeLen <= 18 || (minus && storeLen <= 19));
+    }
+
+    boolean isIntegral() {
+        return !fracOrExp || getBigDecimal().scale() == 0;
+    }
+
+    @Override
+    public void close() throws IOException {
+        reader.close();
+        bufferPool.recycle(buf);
+    }
+
+    private JsonParsingException unexpectedChar(int ch) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+            JsonMessages.TOKENIZER_UNEXPECTED_CHAR(ch, location), location);
+    }
+
+    private JsonParsingException expectedChar(int unexpected, char expected) {
+        JsonLocation location = getLastCharLocation();
+        return new JsonParsingException(
+                JsonMessages.TOKENIZER_EXPECTED_CHAR(unexpected, location, expected), location);
+    }
+    
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonUtil.java b/impl/src/main/java/org/glassfish/json/JsonUtil.java
new file mode 100644
index 0000000..f971522
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonUtil.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import java.io.StringReader;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonParsingException;
+import org.glassfish.json.api.BufferPool;
+
+/**
+ * A utility class
+ * 
+ * @since 1.1
+ */
+public final class JsonUtil {
+
+    private static BufferPool internalPool;
+
+    private JsonUtil() {
+    }
+
+    static BufferPool getInternalBufferPool() {
+        if (internalPool == null) {
+            internalPool = new BufferPoolImpl();
+        }
+        return internalPool;
+    }
+
+    /**
+     * Reads the input JSON text and returns a JsonValue.
+     * <p>For convenience, single quotes as well as double quotes
+     * are allowed to delimit JSON strings. If single quotes are
+     * used, any quotes, single or double, in the JSON string must be
+     * escaped (prepend with a '\').
+     *
+     * @param jsonString the input JSON data
+     * @return the object model for {@code jsonString}
+     * @throws JsonParsingException if the input is not legal JSON text
+     */
+    public static JsonValue toJson(String jsonString) {
+        StringBuilder builder = new StringBuilder();
+        boolean single_context = false;
+        for (int i = 0; i < jsonString.length(); i++) {
+            char ch = jsonString.charAt(i);
+            if (ch == '\\') {
+                i = i + 1;
+                if (i < jsonString.length()) {
+                    ch = jsonString.charAt(i);
+                    if (!(single_context && ch == '\'')) {
+                        // unescape ' inside single quotes
+                        builder.append('\\');
+                    }
+                }
+            } else if (ch == '\'') {
+                // Turn ' into ", for proper JSON string
+                ch = '"';
+                single_context = ! single_context;
+            }
+            builder.append(ch);
+        }
+                   
+        JsonReader reader = new JsonReaderImpl(
+                                new StringReader(builder.toString()),
+                                getInternalBufferPool());
+        JsonValue value = reader.readValue();
+        reader.close();
+        return value;
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java
new file mode 100644
index 0000000..b1482f8
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonWriter;
+import jakarta.json.JsonWriterFactory;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+class JsonWriterFactoryImpl implements JsonWriterFactory {
+    private final Map<String, ?> config;        // unmodifiable map
+    private final boolean prettyPrinting;
+    private final BufferPool bufferPool;
+
+    JsonWriterFactoryImpl(Map<String, ?> config, boolean prettyPrinting,
+            BufferPool bufferPool) {
+        this.config = config;
+        this.prettyPrinting = prettyPrinting;
+        this.bufferPool = bufferPool;
+    }
+
+    @Override
+    public JsonWriter createWriter(Writer writer) {
+        return new JsonWriterImpl(writer, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out) {
+        return new JsonWriterImpl(out, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public JsonWriter createWriter(OutputStream out, Charset charset) {
+        return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return config;
+    }
+}
diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java
new file mode 100644
index 0000000..d7cf86d
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.*;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * JsonWriter impl using generator.
+ *
+ * @author Jitendra Kotamraju
+ */
+class JsonWriterImpl implements JsonWriter {
+
+    private final JsonGeneratorImpl generator;
+    private boolean writeDone;
+    private final NoFlushOutputStream os;
+
+    JsonWriterImpl(Writer writer, BufferPool bufferPool) {
+        this(writer, false, bufferPool);
+    }
+
+    JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool) {
+        generator = prettyPrinting
+                ? new JsonPrettyGeneratorImpl(writer, bufferPool)
+                : new JsonGeneratorImpl(writer, bufferPool);
+        os = null;
+    }
+
+    JsonWriterImpl(OutputStream out, BufferPool bufferPool) {
+        this(out, StandardCharsets.UTF_8, false, bufferPool);
+    }
+
+    JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) {
+        this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool);
+    }
+
+    JsonWriterImpl(OutputStream out, Charset charset,
+                   boolean prettyPrinting, BufferPool bufferPool) {
+        // Decorating the given stream, so that buffered contents can be
+        // written without actually flushing the stream.
+        this.os = new NoFlushOutputStream(out);
+        generator = prettyPrinting
+                ? new JsonPrettyGeneratorImpl(os, charset, bufferPool)
+                : new JsonGeneratorImpl(os, charset, bufferPool);
+    }
+
+    @Override
+    public void writeArray(JsonArray array) {
+        if (writeDone) {
+            throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED());
+        }
+        writeDone = true;
+        generator.writeStartArray();
+        for(JsonValue value : array) {
+            generator.write(value);
+        }
+        generator.writeEnd();
+        // Flush the generator's buffered contents. This won't work for byte
+        // streams as intermediary OutputStreamWriter buffers.
+        generator.flushBuffer();
+        // Flush buffered contents but not the byte stream. generator.flush()
+        // does OutputStreamWriter#flushBuffer (package private) and underlying
+        // byte stream#flush(). Here underlying stream's flush() is no-op.
+        if (os != null) {
+            generator.flush();
+        }
+    }
+
+    @Override
+    public void writeObject(JsonObject object) {
+        if (writeDone) {
+            throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED());
+        }
+        writeDone = true;
+        generator.writeStartObject();
+        for(Map.Entry<String, JsonValue> e : object.entrySet()) {
+            generator.write(e.getKey(), e.getValue());
+        }
+        generator.writeEnd();
+        // Flush the generator's buffered contents. This won't work for byte
+        // streams as intermediary OutputStreamWriter buffers.
+        generator.flushBuffer();
+        // Flush buffered contents but not the byte stream. generator.flush()
+        // does OutputStreamWriter#flushBuffer (package private) and underlying
+        // byte stream#flush(). Here underlying stream's flush() is no-op.
+        if (os != null) {
+            generator.flush();
+        }
+    }
+
+    @Override
+    public void write(JsonStructure value) {
+        if (value instanceof JsonArray) {
+            writeArray((JsonArray)value);
+        } else {
+            writeObject((JsonObject)value);
+        }
+    }
+
+    @Override
+    public void write(JsonValue value) {
+        switch (value.getValueType()) {
+            case OBJECT:
+                writeObject((JsonObject) value);
+                return;
+            case ARRAY:
+                writeArray((JsonArray) value);
+                return;
+            default:
+                if (writeDone) {
+                    throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED());
+                }
+                writeDone = true;
+                generator.write(value);
+                generator.flushBuffer();
+                if (os != null) {
+                    generator.flush();
+                }
+        }
+    }
+
+    @Override
+    public void close() {
+        writeDone = true;
+        generator.close();
+    }
+
+    private static final class NoFlushOutputStream extends FilterOutputStream {
+        public NoFlushOutputStream(OutputStream out) {
+            super(out);
+        }
+
+        @Override
+        public void write(byte b[], int off, int len) throws IOException {
+            out.write(b, off ,len);
+        }
+
+        @Override
+        public void flush() {
+            // no-op
+        }
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/MapUtil.java b/impl/src/main/java/org/glassfish/json/MapUtil.java
new file mode 100644
index 0000000..b28a1ff
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/MapUtil.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Util for transforming a Map to a Json objects.
+ *
+ * @author asotobu
+ */
+public final class MapUtil {
+
+    private MapUtil() {
+        super();
+    }
+
+    static JsonValue handle(Object value, BufferPool bufferPool) {
+        if (value == null) {
+            return JsonValue.NULL;
+        } else if (value instanceof JsonValue) {
+            return (JsonValue) value;
+        } else if (value instanceof JsonArrayBuilder) {
+            return ((JsonArrayBuilder) value).build();
+        } else if (value instanceof JsonObjectBuilder) {
+            return ((JsonObjectBuilder) value).build();
+        } else if (value instanceof BigDecimal) {
+            return JsonNumberImpl.getJsonNumber((BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            return JsonNumberImpl.getJsonNumber((BigInteger) value);
+        } else if (value instanceof Boolean) {
+            Boolean b = (Boolean) value;
+            return b ? JsonValue.TRUE : JsonValue.FALSE;
+        } else if (value instanceof Double) {
+            return JsonNumberImpl.getJsonNumber((Double) value);
+        } else if (value instanceof Integer) {
+            return JsonNumberImpl.getJsonNumber((Integer) value);
+        } else if (value instanceof Long) {
+            return JsonNumberImpl.getJsonNumber((Long) value);
+        } else if (value instanceof String) {
+            return new JsonStringImpl((String) value);
+        } else if (value instanceof Collection) {
+            @SuppressWarnings("unchecked")
+            Collection<?> collection = (Collection<?>) value;
+            JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, bufferPool);
+            return jsonArrayBuilder.build();
+        } else if (value instanceof Map) {
+            @SuppressWarnings("unchecked")
+            JsonObjectBuilder object = new JsonObjectBuilderImpl((Map<String, Object>) value, bufferPool);
+            return object.build();
+        }
+
+        throw new IllegalArgumentException(String.format("Type %s is not supported.", value.getClass()));
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/NodeReference.java b/impl/src/main/java/org/glassfish/json/NodeReference.java
new file mode 100644
index 0000000..255dbd0
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/NodeReference.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+/**
+ * This class is a helper class for JsonPointer implementation,
+ * and is not part of the API.
+ *
+ * This class encapsulates a reference to a JSON node.
+ * There are three types of references.
+ * <ol><li>a reference to the root of a JSON tree.</li>
+ *     <li>a reference to a name/value (possibly non-existing) pair of a JSON object, identified by a name.</li>
+ *     <li>a reference to a member value of a JSON array, identified by an index.</li>
+ * </ol>
+ * Static factory methods are provided for creating these references.
+ *
+ * <p>A referenced value can be retrieved or replaced.
+ * The value of a JSON object or JSON array can be
+ * removed.  A new value can be added to a JSON object or
+ * inserted into a JSON array</p>
+ *
+ * <p>Since a {@code JsonObject} or {@code JsonArray} is immutable, these operations
+ * must not modify the referenced JSON object or array. The methods {@link #add},
+ * {@link #replace}, and {@link #remove} returns a new
+ * JSON object or array after the execution of the operation.</p>
+ */
+abstract class NodeReference {
+
+    /**
+     * Return {@code true} if a reference points to a valid value, {@code false} otherwise.
+     *
+     * @return {@code true} if a reference points to a value
+     */
+    abstract public boolean contains();
+
+    /**
+     * Get the value at the referenced location.
+     *
+     * @return the JSON value referenced
+     * @throws JsonException if the referenced value does not exist
+     */
+    abstract public JsonValue get();
+
+    /**
+     * Add or replace a value at the referenced location.
+     * If the reference is the root of a JSON tree, the added value must be
+     * a JSON object or array, which becomes the referenced JSON value.
+     * If the reference is an index of a JSON array, the value is inserted
+     * into the array at the index.  If the index is -1, the value is
+     * appended to the array.
+     * If the reference is a name of a JSON object, the name/value pair is added
+     * to the object, replacing any pair with the same name.
+     *
+     * @param value the value to be added
+     * @return the JsonStructure after the operation
+     * @throws JsonException if the index to the array is not -1 or is out of range
+     */
+    abstract public JsonStructure add(JsonValue value);
+
+    /**
+     * Remove the name/value pair from the JSON object, or the value in a JSON array, as specified by the reference
+     *
+     * @return the JsonStructure after the operation
+     * @throws JsonException if the name/value pair of the referenced JSON object
+     *    does not exist, or if the index of the referenced JSON array is
+     *    out of range, or if the reference is a root reference
+     */
+    abstract public JsonStructure remove();
+
+    /**
+     * Replace the referenced value with the specified value.
+     *
+     * @param value the JSON value to be stored at the referenced location
+     * @return the JsonStructure after the operation
+     * @throws JsonException if the name/value pair of the referenced JSON object
+     *    does not exist, or if the index of the referenced JSON array is
+     *    out of range, or if the reference is a root reference
+     */
+    abstract public JsonStructure replace(JsonValue value);
+
+    /**
+     * Returns a {@code NodeReference} for a {@code JsonStructure}.
+     *
+     * @param structure the {@code JsonStructure} referenced
+     * @return the {@code NodeReference}
+     */
+    public static NodeReference of(JsonStructure structure) {
+        return new RootReference(structure);
+    }
+
+    /**
+     * Returns a {@code NodeReference} for a name/value pair in a
+     * JSON object.
+     *
+     * @param object the referenced JSON object
+     * @param name the name of the name/pair
+     * @return the {@code NodeReference}
+     */
+    public static NodeReference of(JsonObject object, String name) {
+        return new ObjectReference(object, name);
+    }
+
+    /**
+     * Returns a {@code NodeReference} for a member value in a
+     * JSON array.
+     *
+     * @param array the referenced JSON array
+     * @param index the index of the member value in the JSON array
+     * @return the {@code NodeReference}
+     */
+    public static NodeReference of(JsonArray array, int index) {
+        return new ArrayReference(array, index);
+    }
+
+    static class RootReference extends NodeReference {
+
+        private JsonStructure root;
+
+        RootReference(JsonStructure root) {
+            this.root = root;
+        }
+
+        @Override
+        public boolean contains() {
+            return root != null;
+        }
+
+        @Override
+        public JsonValue get() {
+            return root;
+        }
+
+        @Override
+        public JsonStructure add(JsonValue value) {
+            switch (value.getValueType() ) {
+                case OBJECT:
+                case ARRAY:
+                    this.root = (JsonStructure) value;
+                    break;
+                default:
+                    throw new JsonException(JsonMessages.NODEREF_VALUE_ADD_ERR());
+            }
+            return root;
+        }
+
+        @Override
+        public JsonStructure remove() {
+            throw new JsonException(JsonMessages.NODEREF_VALUE_CANNOT_REMOVE());
+        }
+
+        @Override
+        public JsonStructure replace(JsonValue value) {
+            return add(value);
+        }
+    }
+
+    static class ObjectReference extends NodeReference {
+
+        private final JsonObject object;
+        private final String key;
+
+        ObjectReference(JsonObject object, String key) {
+            this.object = object;
+            this.key = key;
+        }
+
+        @Override
+        public boolean contains() {
+            return object != null && object.containsKey(key);
+        }
+
+        @Override
+        public JsonValue get() {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key));
+            }
+            return object.get(key);
+        }
+
+        @Override
+        public JsonObject add(JsonValue value) {
+            return new JsonObjectBuilderImpl(object, JsonUtil.getInternalBufferPool()).add(key, value).build();
+        }
+
+        @Override
+        public JsonObject remove() {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key));
+            }
+            return new JsonObjectBuilderImpl(object, JsonUtil.getInternalBufferPool()).remove(key).build();
+        }
+
+        @Override
+        public JsonObject replace(JsonValue value) {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key));
+            }
+            return add(value);
+        }
+    }
+
+    static class ArrayReference extends NodeReference {
+
+        private final JsonArray array;
+        private final int index; // -1 means "-" in JSON Pointer
+
+        ArrayReference(JsonArray array, int index) {
+            this.array = array;
+            this.index = index;
+        }
+
+        @Override
+        public boolean contains() {
+            return array != null && index > -1 && index < array.size();
+        }
+
+        @Override
+        public JsonValue get() {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size()));
+            }
+            return array.get(index);
+        }
+
+        @Override
+        public JsonArray add(JsonValue value) {
+            //TODO should we check for arrayoutofbounds?
+            // The spec seems to say index = array.size() is allowed. This is handled as append
+            JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool());
+            if (index == -1 || index == array.size()) {
+                builder.add(value);
+            } else {
+                if(index < array.size()) {
+                    builder.add(index, value);
+                } else {
+                    throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size()));
+                }
+            }
+            return builder.build();
+        }
+
+        @Override
+        public JsonArray remove() {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size()));
+            }
+            JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool());
+            return builder.remove(index).build();
+        }
+
+        @Override
+        public JsonArray replace(JsonValue value) {
+            if (!contains()) {
+                throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size()));
+            }
+            JsonArrayBuilder builder = new JsonArrayBuilderImpl(this.array, JsonUtil.getInternalBufferPool());
+            return builder.set(index, value).build();
+        }
+    }
+}
+
diff --git a/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java b/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java
new file mode 100644
index 0000000..d40d45e
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json;
+
+import jakarta.json.JsonException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * A filter stream that detects the unicode encoding for the original
+ * stream
+ *
+ * @author Jitendra Kotamraju
+ */
+class UnicodeDetectingInputStream extends FilterInputStream {
+
+    private static final Charset UTF_32LE = Charset.forName("UTF-32LE");
+    private static final Charset UTF_32BE = Charset.forName("UTF-32BE");
+
+    private static final byte FF = (byte)0xFF;
+    private static final byte FE = (byte)0xFE;
+    private static final byte EF = (byte)0xEF;
+    private static final byte BB = (byte)0xBB;
+    private static final byte BF = (byte)0xBF;
+    private static final byte NUL = (byte)0x00;
+
+    private final byte[] buf = new byte[4];
+    private int bufLen;
+    private int curIndex;
+    private final Charset charset;
+
+    UnicodeDetectingInputStream(InputStream is) {
+        super(is);
+        charset = detectEncoding();
+    }
+
+    Charset getCharset() {
+        return charset;
+    }
+
+    private void fillBuf() {
+        int b1;
+        int b2;
+        int b3;
+        int b4;
+
+        try {
+            b1 = in.read();
+            if (b1 == -1) {
+                return;
+            }
+
+            b2 = in.read();
+            if (b2 == -1) {
+                bufLen = 1;
+                buf[0] = (byte)b1;
+                return;
+            }
+
+            b3 = in.read();
+            if (b3 == -1) {
+                bufLen = 2;
+                buf[0] = (byte)b1;
+                buf[1] = (byte)b2;
+                return;
+            }
+
+            b4 = in.read();
+            if (b4 == -1) {
+                bufLen = 3;
+                buf[0] = (byte)b1;
+                buf[1] = (byte)b2;
+                buf[2] = (byte)b3;
+                return;
+            }
+            bufLen = 4;
+            buf[0] = (byte)b1;
+            buf[1] = (byte)b2;
+            buf[2] = (byte)b3;
+            buf[3] = (byte)b4;
+        } catch (IOException ioe) {
+            throw new JsonException(JsonMessages.PARSER_INPUT_ENC_DETECT_IOERR(), ioe);
+        }
+    }
+
+    private Charset detectEncoding() {
+        fillBuf();
+        if (bufLen < 2) {
+            throw new JsonException(JsonMessages.PARSER_INPUT_ENC_DETECT_FAILED());
+        } else if (bufLen == 4) {
+            // Use BOM to detect encoding
+            if (buf[0] == NUL && buf[1] == NUL && buf[2] == FE && buf[3] == FF) {
+                curIndex = 4;
+                return UTF_32BE;
+            } else if (buf[0] == FF && buf[1] == FE && buf[2] == NUL && buf[3] == NUL) {
+                curIndex = 4;
+                return UTF_32LE;
+            } else if (buf[0] == FE && buf[1] == FF) {
+                curIndex = 2;
+                return StandardCharsets.UTF_16BE;
+            } else if (buf[0] == FF && buf[1] == FE) {
+                curIndex = 2;
+                return StandardCharsets.UTF_16LE;
+            } else if (buf[0] == EF && buf[1] == BB && buf[2] == BF) {
+                curIndex = 3;
+                return StandardCharsets.UTF_8;
+            }
+            // No BOM, just use JSON RFC's encoding algo to auto-detect
+            if (buf[0] == NUL && buf[1] == NUL && buf[2] == NUL) {
+                return UTF_32BE;
+            } else if (buf[0] == NUL && buf[2] == NUL) {
+                return StandardCharsets.UTF_16BE;
+            } else if (buf[1] == NUL && buf[2] == NUL && buf[3] == NUL) {
+                return UTF_32LE;
+            } else if (buf[1] == NUL && buf[3] == NUL) {
+                return StandardCharsets.UTF_16LE;
+            }
+        }
+        return StandardCharsets.UTF_8;
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (curIndex < bufLen) {
+            return buf[curIndex++];
+        }
+        return in.read();
+    }
+
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        if (curIndex < bufLen) {
+            if (len == 0) {
+                return 0;
+            }
+            if (off < 0 || len < 0 || len > b.length -off) {
+                throw new IndexOutOfBoundsException();
+            }
+            int min = Math.min(bufLen-curIndex, len);
+            System.arraycopy(buf, curIndex, b, off, min);
+            curIndex += min;
+            return min;
+        }
+        return in.read(b, off, len);
+    }
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/api/BufferPool.java b/impl/src/main/java/org/glassfish/json/api/BufferPool.java
new file mode 100644
index 0000000..1141ff0
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/api/BufferPool.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.api;
+
+/**
+ * char[] pool that pool instances of char[] which are expensive to create.
+ *
+ * @author Jitendra Kotamraju
+ */
+public interface BufferPool {
+
+    /**
+     * Gets a new char[] object from the pool.
+     *
+     * <p>
+     * If no object is available in the pool, this method creates a new one.
+     *
+     * @return
+     *      always non-null.
+     */
+    char[] take();
+
+    /**
+     * Returns an object back to the pool.
+     *
+     * @param buf object to return back to the pool
+     */
+    void recycle(char[] buf);
+
+}
diff --git a/impl/src/main/java/org/glassfish/json/api/JsonConfig.java b/impl/src/main/java/org/glassfish/json/api/JsonConfig.java
new file mode 100644
index 0000000..f7a160f
--- /dev/null
+++ b/impl/src/main/java/org/glassfish/json/api/JsonConfig.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.api;
+
+public interface JsonConfig {
+    /**
+     * Configuration property to reject duplicate keys. The value of the property could be
+     * be anything.
+     */
+    String REJECT_DUPLICATE_KEYS = "org.glassfish.json.rejectDuplicateKeys";
+}
diff --git a/impl/src/main/javadoc/doc-files/speclicense.html b/impl/src/main/javadoc/doc-files/speclicense.html
new file mode 100644
index 0000000..ba29e5e
--- /dev/null
+++ b/impl/src/main/javadoc/doc-files/speclicense.html
@@ -0,0 +1,72 @@
+<html>
+<head>
+<title>Eclipse Foundation Specification License - v1.0</title>
+</head>
+<body>
+<h1>Eclipse Foundation Specification License - v1.0</h1>
+<p>By using and/or copying this document, or the Eclipse Foundation
+  document from which this statement is linked, you (the licensee) agree
+  that you have read, understood, and will comply with the following
+  terms and conditions:</p>
+
+<p>Permission to copy, and distribute the contents of this document, or
+  the Eclipse Foundation document from which this statement is linked, in
+  any medium for any purpose and without fee or royalty is hereby
+  granted, provided that you include the following on ALL copies of the
+  document, or portions thereof, that you use:</p>
+
+<ul>
+  <li> link or URL to the original Eclipse Foundation document.</li>
+  <li>All existing copyright notices, or if one does not exist, a notice
+      (hypertext is preferred, but a textual representation is permitted)
+      of the form: &quot;Copyright &copy; [$date-of-document]
+      &ldquo;Eclipse Foundation, Inc. &lt;&lt;url to this license&gt;&gt;
+      &quot;
+  </li>
+</ul>
+
+<p>Inclusion of the full text of this NOTICE must be provided. We
+  request that authorship attribution be provided in any software,
+  documents, or other items or products that you create pursuant to the
+  implementation of the contents of this document, or any portion
+  thereof.</p>
+
+<p>No right to create modifications or derivatives of Eclipse Foundation
+  documents is granted pursuant to this license, except anyone may
+  prepare and distribute derivative works and portions of this document
+  in software that implements the specification, in supporting materials
+  accompanying such software, and in documentation of such software,
+  PROVIDED that all such works include the notice below. HOWEVER, the
+  publication of derivative works of this document for use as a technical
+  specification is expressly prohibited.</p>
+
+<p>The notice is:</p>
+
+<p>&quot;Copyright &copy; 2018 Eclipse Foundation. This software or
+  document includes material copied from or derived from [title and URI
+  of the Eclipse Foundation specification document].&quot;</p>
+
+<h2>Disclaimers</h2>
+
+<p>THIS DOCUMENT IS PROVIDED &quot;AS IS,&quot; AND THE COPYRIGHT
+  HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR
+  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+  NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE
+  SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS
+  WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR
+  OTHER RIGHTS.</p>
+
+<p>THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE
+  FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT
+  OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE
+  CONTENTS THEREOF.</p>
+
+<p>The name and trademarks of the copyright holders or the Eclipse
+  Foundation may NOT be used in advertising or publicity pertaining to
+  this document or its contents without specific, written prior
+  permission. Title to copyright in this document will at all times
+  remain with copyright holders.</p>
+
+</body>
+</html>
diff --git a/impl/src/main/javadoc/overview.html b/impl/src/main/javadoc/overview.html
new file mode 100644
index 0000000..2b82208
--- /dev/null
+++ b/impl/src/main/javadoc/overview.html
@@ -0,0 +1,108 @@
+<html>
+<!--
+
+    Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<body>
+Jakarta JSON Processing provides portable APIs to parse,
+generate, transform, and query <a href="http://json.org/">JSON</a> using the
+streaming API or the object model API.
+
+<p>The Streaming API provides a way to parsing and generation of JSON in a
+streaming fashion. It hands over parsing and generation control to the
+programmer. The streaming API provides an event-based parser and allows an
+application developer to ask for the next event rather than handling the event
+in a callback. This gives a developer more procedural control over
+the processing of the JSON. Application code can process or discard
+the parser event, and ask for the next event(pull the event). The
+streaming model is adequate for local processing where random access of other
+parts of the data is not required. Similarly, the streaming API provides
+a way to generate well-formed JSON to a stream by writing one event at a time.
+
+<p>The object model API creates a random access tree-like structure that
+represents the JSON data in memory. The tree can then be navigated and
+queried. This programming model is the most flexible and enables processing
+that requires random access to the complete contents of the tree. However,
+it is often not as efficient as the streaming model and requires more memory.
+The object model generates JSON output by navigating the entire tree at once.
+
+<h1>The Streaming API</h1>
+<p>The streaming API is similar to the StAX API for XML and consists of the
+interfaces {@link jakarta.json.stream.JsonParser} and
+{@link jakarta.json.stream.JsonGenerator}. {@code JsonParser}
+contains methods to parse JSON data using the streaming model.
+{@code JsonGenerator} contains methods to write JSON data to an ouptut source.
+
+<p>{@code JsonParser} provides forward, read-only access to
+JSON data using the pull parsing programming model. In this model the
+application code controls the thread and calls methods in the parser interface
+to move the parser forward or to obtain JSON data from the current state of
+the parser. Refer to
+<a href="jakarta.json/jakarta/json/stream/JsonParser.html#JsonParserExample2">this example</a>
+for more details.
+
+<p>{@code JsonGenerator} provides methods to write JSON to a stream. The
+generator writes name/value pairs in JSON objects and values in JSON arrays.
+Refer to
+<a href="jakarta.json/jakarta/json/stream/JsonGenerator.html#JsonGeneratorExample3">this
+example</a> for more details.
+
+<p>The streaming API is a low-level API designed to process large amounts of
+JSON data efficiently. Other JSON frameworks (such as JSON binding) can be
+implemented using this API.</p>
+
+<h1>The Object Model API</h1>
+<p>The object model API is similar to the DOM API for XML. It is a high-level
+API that provides immutable object models for JSON object and array structures.
+These JSON structures are represented as object models using the Java types
+{@link jakarta.json.JsonObject} and {@link jakarta.json.JsonArray}.
+{@code JsonObject} provides a {@link java.util.Map} view to access the unordered
+collection of zero or more name/value pairs from the model. Similarly,
+{@code JsonArray} provides a {@link java.util.List} view to access the ordered
+sequence of zero or more values from the model.
+
+<p>The object model API uses builder patterns to create these object models.
+Application code can use the interface {@link jakarta.json.JsonObjectBuilder}
+to create models that represent JSON objects. The resulting model is of type
+{@code JsonObject}. Refer to
+<a href="jakarta.json/jakarta/json/JsonObjectBuilder.html#JsonObjectBuilderExample1">this example</a>
+for more details. Application code can use the interface
+{@link jakarta.json.JsonArrayBuilder} to create models that represent JSON arrays.
+The resulting model is of type {@code JsonArray}. Refer to
+<a href="jakarta.json/jakarta/json/JsonArrayBuilder.html#JsonArrayBuilderExample1">this example</a>
+for more details.
+
+<p>These object models can also be created from an input source (such as
+{@link java.io.InputStream} or {@link java.io.Reader}) using the interface
+{@link jakarta.json.JsonReader}.
+<a href="jakarta.json/jakarta/json/JsonReader.html#JsonReaderExample1">This example</a> shows
+how to read and create an empty {@code JsonArray} model using the interface
+{@code JsonReader}. Similarly, these object models can be written to an output
+source (such as {@link java.io.OutputStream} or {@link java.io.Writer}) using
+the class {@link jakarta.json.JsonWriter}.
+<a href="jakarta.json/jakarta/json/JsonWriter.html#JsonWriterExample1">This example</a> shows
+how to write an empty {@code JsonObject} model using the interface
+{@code JsonWriter}.
+
+<h1>JSON Pointer, JSON Patch, and JSON Merge Patch</h1>
+Jakarta JSON Processing supports the latest standard on
+<a href="http://tools.ietf.org/html/rfc6901">JSON Pointer</a>,
+<a Href="http://tools.ietf.org/html/rfc6902">JSON Patch</a>, and
+<a Href="http://tools.ietf.org/html/rfc7396">JSON Merge Patch</a>.
+
+</body>
+</html>
diff --git a/impl/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider b/impl/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
new file mode 100644
index 0000000..52f1a6b
--- /dev/null
+++ b/impl/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
@@ -0,0 +1 @@
+org.glassfish.json.JsonProviderImpl
diff --git a/impl/src/main/resources/org/glassfish/json/messages.properties b/impl/src/main/resources/org/glassfish/json/messages.properties
new file mode 100644
index 0000000..1c69310
--- /dev/null
+++ b/impl/src/main/resources/org/glassfish/json/messages.properties
@@ -0,0 +1,92 @@
+#
+# Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+
+
+
+internal.error=Internal Error
+
+parser.getString.err=JsonParser#getString() is valid only for KEY_NAME, VALUE_STRING, VALUE_NUMBER parser states. \
+  But current parser state is {0}
+parser.isIntegralNumber.err=JsonParser#isIntegralNumber() is valid only for VALUE_NUMBER parser state. \
+  But current parser state is {0}
+parser.getInt.err=JsonParser#getInt() is valid only for VALUE_NUMBER parser state. \
+  But current parser state is {0}
+parser.getLong.err=JsonParser#getLong() is valid only for VALUE_NUMBER parser state. \
+  But current parser state is {0}
+parser.getBigDecimal.err=JsonParser#getBigDecimal() is valid only for VALUE_NUMBER parser state. \
+  But current parser state is {0}
+parser.getArray.err=JsonParser#getArray() or JsonParser#getArrayStream() is valid only for START_ARRAY parser state. \
+  But current parser state is {0}
+parser.getObject.err=JsonParser#getObject() or JsonParser#getObjectStream() is valid only for START_OBJECT parser state. \
+  But current parser state is {0}
+parser.getValue.err=JsonParser#getValue() is valid only for START_ARRAY, START_OBJECT, KEY_NAME, VALUE_STRING, VALUE_NUMBER, VALUE_NULL, VALUE_FALSE, VALUE_TRUE parser states. \
+  But current parser state is {0}
+parser.getValueStream.err=JsonParser#getValueStream() the parser must not be in an array or object. \
+  But current parser state is {0}
+parser.expected.eof=Expected EOF token, but got {0}
+parser.tokenizer.close.io=I/O error while closing JSON tokenizer
+parser.invalid.token=Invalid token={0} at {1}. Expected tokens are: {2}
+parser.state.err=Unknown value type {0}
+parser.scope.err=Cannot be called for value {0}
+parser.input.enc.detect.failed=Cannot auto-detect encoding, not enough chars
+parser.input.enc.detect.ioerr=I/O error while auto-detecting the encoding of stream
+parser.duplicate.key=Duplicate key ''{0}'' is not allowed
+
+generator.flush.io.err=I/O error while flushing generated JSON
+generator.close.io.err=I/O error while closing JsonGenerator
+generator.write.io.err=I/O error while writing in JsonGenerator
+generator.illegal.method=Illegal method during JSON generation, \
+  not valid in current context {0}
+generator.double.infinite.nan=double value cannot be Infinite or NaN
+generator.incomplete.json=Generating incomplete JSON
+generator.illegal.multiple.text=Cannot generate more than one JSON text
+
+writer.write.already.called=write/writeObject/writeArray/close method is already called
+
+reader.read.already.called=read/readObject/readArray/close method is already called
+
+objbuilder.name.null=Name in JsonObject's name/value pair cannot be null
+objbuilder.value.null=Value in JsonObject's name/value pair cannot be null
+objbuilder.object.builder.null=Object builder that is used to create a value in JsonObject's name/value pair cannot be null
+objbuilder.array.builder.null=Array builder that is used to create a value in JsonObject's name/value pair cannot be null
+
+arrbuilder.value.null=Cannot invoke add(null) while building JsonArray.
+arrbuilder.object.builder.null=Object builder that is used to add a value to JSON array cannot be null
+arrbuilder.array.builder.null=Array builder that is used to add a value to JSON array cannot be null
+arrbuilder.valuelist.null=Index: {0}, Size: {1}
+
+tokenizer.unexpected.char=Unexpected char {0} at {1}
+tokenizer.expected.char=Unexpected char {0} at {1}, expecting ''{2}''
+tokenizer.io.err=I/O error while parsing JSON
+
+pointer.format.invalid=A non-empty JSON Pointer must begin with a ''/''
+pointer.mapping.missing=The JSON Object ''{0}'' contains no mapping for the name ''{1}''
+pointer.reference.invalid=The reference value in a JSON Pointer must be a JSON Object or a JSON Array, was ''{0}''
+pointer.array.index.err=Array index format error, was ''{0}''
+pointer.array.index.illegal=Illegal integer format, was ''{0}''
+
+noderef.value.add.err=The root value only allows adding a JSON object or array
+noderef.value.cannot.remove=The JSON value at the root cannot be removed
+noderef.object.missing=Non-existing name/value pair in the object for key {0}
+noderef.array.index.err=An array item index is out of range. Index: {0}, Size: {1}
+
+patch.must.be.array=A JSON Patch must be an array of JSON Objects
+patch.move.proper.prefix=The ''{0}'' path of the patch operation ''move'' is a proper prefix of the ''{1}'' path
+patch.move.target.null=The ''{0}'' path of the patch operation ''move'' does not exist in target object
+patch.test.failed=The JSON Patch operation ''test'' failed for path ''{0}'' and value ''{1}''
+patch.illegal.operation=Illegal value for the op member of the JSON Patch operation: ''{0}''
+patch.member.missing=The JSON Patch operation ''{0}'' must contain a ''{1}'' member
+patch.operation.missing=The JSON Patch must contain ''op'' member
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonArrayTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonArrayTest.java
new file mode 100644
index 0000000..fa2d9d1
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonArrayTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonArrayTest extends TestCase {
+    public JsonArrayTest(String testName) {
+        super(testName);
+    }
+
+    public void testArrayEquals() throws Exception {
+        JsonArray expected = Json.createArrayBuilder()
+                .add(JsonValue.TRUE)
+                .add(JsonValue.FALSE)
+                .add(JsonValue.NULL)
+                .add(Integer.MAX_VALUE)
+                .add(Long.MAX_VALUE)
+                .add(Double.MAX_VALUE)
+                .add(Integer.MIN_VALUE)
+                .add(Long.MIN_VALUE)
+                .add(Double.MIN_VALUE)
+                .add(Json.createArrayBuilder().add("abc"))
+                .add(Json.createObjectBuilder().add("one", 1))
+                .build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.writeArray(expected);
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonArray actual = reader.readArray();
+        reader.close();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testArrayEqualsUsingCollection() {
+        List<Object> list = new ArrayList<>();
+        list.add(JsonValue.TRUE);
+        list.add(JsonValue.FALSE);
+        list.add(JsonValue.NULL);
+        list.add(Integer.MAX_VALUE);
+        list.add(Long.MAX_VALUE);
+        list.add(Double.MAX_VALUE);
+        list.add(Integer.MIN_VALUE);
+        list.add(Long.MIN_VALUE);
+        list.add(Double.MIN_VALUE);
+        list.add(Json.createArrayBuilder().add("abc"));
+        list.add(Json.createObjectBuilder().add("one", 1));
+
+        JsonArray expected = Json.createArrayBuilder(list).build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.writeArray(expected);
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonArray actual = reader.readArray();
+        reader.close();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testStringValue() throws Exception {
+        JsonArray array = Json.createArrayBuilder()
+                .add("John")
+                .build();
+        assertEquals("John", array.getString(0));
+    }
+
+    public void testIntValue() throws Exception {
+        JsonArray array = Json.createArrayBuilder()
+                .add(20)
+                .build();
+        assertEquals(20, array.getInt(0));
+    }
+
+    public void testAdd() {
+        JsonArray array = Json.createArrayBuilder().build();
+        try {
+            array.add(JsonValue.FALSE);
+            fail("JsonArray#add() should throw UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    public void testRemove() {
+        JsonArray array = Json.createArrayBuilder().build();
+        try {
+            array.remove(0);
+            fail("JsonArray#remove() should throw UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    public void testNumberView() throws Exception {
+        JsonArray array = Json.createArrayBuilder().add(20).add(10).build();
+
+        List<JsonNumber> numberList = array.getValuesAs(JsonNumber.class);
+        for(JsonNumber num : numberList) {
+            num.intValue();
+        }
+
+        assertEquals(20, array.getInt(0));
+        assertEquals(10, array.getInt(1));
+    }
+
+    public void testArrayBuilderNpe() {
+        try {
+            JsonArray array = Json.createArrayBuilder().add((JsonValue)null).build();
+            fail("JsonArrayBuilder#add(null) should throw NullPointerException");
+        } catch(NullPointerException e) {
+            // Expected
+        }
+    }
+
+    public void testHashCode() {
+        JsonArray array1 = Json.createArrayBuilder().add(1).add(2).add(3).build();
+        assertTrue(array1.hashCode() == array1.hashCode()); //1st call compute hashCode, 2nd call returns cached value
+
+        JsonArray array2 = Json.createArrayBuilder().add(1).add(2).add(3).build();
+        assertTrue(array1.hashCode() == array2.hashCode());
+
+        JsonArray array3 = Json.createArrayBuilder().build(); //org.glassfish.json.JsonArrayBuilderImpl.JsonArrayImpl
+        JsonArray array4 = JsonValue.EMPTY_JSON_ARRAY; //jakarta.json.EmptyArray
+
+        assertTrue(array3.equals(array4));
+        assertTrue(array3.hashCode() == array4.hashCode()); //equal instances have same hashCode
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java
new file mode 100644
index 0000000..c41881b
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import java.util.Map;
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author lukas
+ */
+public class JsonBuilderFactoryTest {
+    
+    @Test
+    public void testArrayBuilder() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        Assert.assertNotNull(builderFactory.createArrayBuilder());
+    }
+    
+    @Test(expected = NullPointerException.class)
+    public void testArrayBuilderNPE() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        builderFactory.createArrayBuilder(null);
+    }
+
+    @Test
+    public void testArrayBuilderFromArray() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        JsonArrayBuilder builder = builderFactory.createArrayBuilder(JsonBuilderTest.buildPhone());
+        Assert.assertEquals(JsonBuilderTest.buildPhone(), builder.build());
+    }
+
+    @Test
+    public void testObjectBuilder() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        Assert.assertNotNull(builderFactory.createObjectBuilder());
+    }
+    
+    @Test(expected = NullPointerException.class)
+    public void testObjectBuilderNPE() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        builderFactory.createObjectBuilder((JsonObject) null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testObjectBuilderNPE_map() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        builderFactory.createObjectBuilder((Map<String, Object>) null);
+    }
+
+    @Test
+    public void testObjectBuilderFromObject() {
+        JsonBuilderFactory builderFactory = Json.createBuilderFactory(null);
+        JsonObjectBuilder builder = builderFactory.createObjectBuilder(JsonBuilderTest.buildPerson());
+        Assert.assertEquals(JsonBuilderTest.buildPerson(), builder.build());
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java
new file mode 100644
index 0000000..20057e7
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonBuilderTest extends TestCase {
+    public JsonBuilderTest(String testName) {
+        super(testName);
+    }
+
+    public void testEmptyObject() throws Exception {
+        JsonObject empty = Json.createObjectBuilder()
+                .build();
+
+        JsonObjectTest.testEmpty(empty);
+    }
+
+    public void testEmptyArray() throws Exception {
+        JsonArray empty = Json.createArrayBuilder()
+                .build();
+
+        assertTrue(empty.isEmpty());
+    }
+
+    public void testObject() throws Exception {
+        JsonObject person = buildPerson();
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testNumber() throws Exception {
+        JsonObject person = buildPerson();
+        JsonNumber number = person.getJsonNumber("age");
+        assertEquals(25, number.intValueExact());
+        assertEquals(25, number.intValue());
+        assertTrue(number.isIntegral());
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testJsonObjectCopy() {
+        JsonObject person = buildPerson();
+        final JsonObjectBuilder objectBuilder = Json.createObjectBuilder(person);
+        final JsonObject copyPerson = objectBuilder.build();
+
+        JsonNumber number = copyPerson.getJsonNumber("age");
+        assertEquals(25, number.intValueExact());
+        assertEquals(25, number.intValue());
+        assertTrue(number.isIntegral());
+        JsonObjectTest.testPerson(copyPerson);
+
+    }
+
+    public void testJsonObjectMap() {
+        Map<String, Object> person = buildPersonAsMap();
+        final JsonObjectBuilder objectBuilder = Json.createObjectBuilder(person);
+        final JsonObject copyPerson = objectBuilder.build();
+
+        JsonNumber number = copyPerson.getJsonNumber("age");
+        assertEquals(25, number.intValueExact());
+        assertEquals(25, number.intValue());
+        assertTrue(number.isIntegral());
+        JsonObjectTest.testPerson(copyPerson);
+    }
+
+    static Map<String, Object> buildPersonAsMap() {
+        Map<String, Object> person = new HashMap<>();
+        person.put("firstName", "John");
+        person.put("lastName", "Smith");
+        person.put("age", 25);
+
+        Map<String, Object> address = Optional.of(new HashMap<String, Object>()).get();
+        address.put("streetAddress", "21 2nd Street");
+        address.put("city", "New York");
+        address.put("state", "NY");
+        address.put("postalCode", "10021");
+
+        person.put("address", address);
+        person.put("mailingAddress", Optional.empty());
+
+        Collection<Map<String, Object>> phones = new ArrayList<>();
+
+        Map<String, Object> phone1 = new HashMap<>();
+        phone1.put("type", "home");
+        phone1.put("number", "212 555-1234");
+        phones.add(phone1);
+
+        Map<String, Object> phone2 = new HashMap<>();
+        phone2.put("type", "fax");
+        phone2.put("number", "646 555-4567");
+        phones.add(phone2);
+
+        person.put("phoneNumber", phones);
+
+        return person;
+    }
+
+    static JsonObject buildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+
+    static JsonObject buildAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+
+    static JsonArray buildPhone() {
+        return Json.createArrayBuilder()
+                .add(Json.createObjectBuilder()
+                        .add("type", "home")
+                        .add("number", "212 555-1234"))
+                .add(Json.createObjectBuilder()
+                        .add("type", "fax")
+                        .add("number", "646 555-4567"))
+                .build();
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java
new file mode 100644
index 0000000..c71ac3d
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonCollectors;
+import org.glassfish.json.JsonUtil;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Some JSON query tests/examples, using Java stream operations, with JSON collectors.
+ * @author Kin-man Chung
+ */
+public class JsonCollectorTest {
+
+    static JsonArray contacts;
+
+    @BeforeClass
+    public static void setUpClass() {
+        // The JSON source
+        contacts = (JsonArray) JsonUtil.toJson(
+        "[                                 " +
+        "  { 'name': 'Duke',               " +
+        "    'age': 18,                    " +
+        "    'gender': 'M',                " +
+        "    'phones': {                   " +
+        "       'home': '650-123-4567',    " +
+        "       'mobile': '650-234-5678'}}," +
+        "  { 'name': 'Jane',               " +
+        "    'age': 23,                    " +
+        "    'gender': 'F',                " +
+        "    'phones': {                   " +
+        "       'mobile': '707-999-5555'}}," +
+        "  { 'name': 'Joanna',             " +
+        "    'gender': 'F',                " +
+        "    'phones': {                   " +
+        "       'mobile': '505-333-4444'}} " +
+        " ]");
+    }
+
+    @Test
+    public void testToJsonArray() {
+        /*
+         * Query: retrieve the names of female contacts
+         * Returns a JsonArray of names
+         */
+        JsonArray result = contacts.getValuesAs(JsonObject.class).stream()
+                   .filter(x->"F".equals(x.getString("gender")))
+                   .map(x-> x.get("name"))
+                   .collect(JsonCollectors.toJsonArray());
+        JsonValue expected = JsonUtil.toJson("['Jane','Joanna']");
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testToJsonObject() {
+        /*
+         * Query: retrieve the names and mobile phones of female contacts
+         * Returns a JsonObject of name phones pairs
+         */
+        JsonObject result = contacts.getValuesAs(JsonObject.class).stream()
+                    .filter(x->"F".equals(x.getString("gender")))
+                    .collect(JsonCollectors.toJsonObject(
+                            x->x.asJsonObject().getString("name"),
+                            x->x.asJsonObject().getJsonObject("phones").get("mobile")))
+                    ;
+        JsonValue expected = JsonUtil.toJson(
+                "{'Jane': '707-999-5555', 'Joanna': '505-333-4444'}");
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testGroupBy() {
+        /*
+         * Query: group the contacts according to gender
+         * Returns a JsonObject, with gender/constacts value pairs
+         */
+        JsonObject result = contacts.getValuesAs(JsonObject.class).stream()
+            .collect(JsonCollectors.groupingBy(x->((JsonObject)x).getString("gender")));
+        JsonValue expected = JsonUtil.toJson(
+        "{'F':                               " +
+        "  [                                 " +
+        "    { 'name': 'Jane',               " +
+        "      'age': 23,                    " +
+        "      'gender': 'F',                " +
+        "      'phones': {                   " +
+        "         'mobile': '707-999-5555'}}," +
+        "    { 'name': 'Joanna',             " +
+        "      'gender': 'F',                " +
+        "      'phones': {                   " +
+        "         'mobile': '505-333-4444'}} " +
+        "  ],                                " +
+        "'M':                                " +
+        "  [                                 " +
+        "    { 'name': 'Duke',               " +
+        "      'age': 18,                    " +
+        "      'gender': 'M',                " +
+        "      'phones': {                   " +
+        "         'home': '650-123-4567',    " +
+        "         'mobile': '650-234-5678'}} " +
+        "  ]                                 " +
+        "}");
+
+        assertEquals(result,expected);
+    }
+
+    static int index; //for keeping track of the array index
+    @Test
+    public void testQueryAndPatch() {
+        /*
+         * Query and patch: Increment the ages of contacts with an age entry
+         * PatchBuilder is used for building the necessary JsonPatch.
+         */
+        index = -1;
+        JsonPatchBuilder builder = Json.createPatchBuilder();
+        contacts.getValuesAs(JsonObject.class).stream()
+            .peek(p->index++)
+            .filter(p->p.containsKey("age"))
+            .forEach(p-> builder.replace("/"+index+"/age", p.getInt("age")+1));
+        JsonArray result = builder.build().apply(contacts);
+
+        JsonValue expected = (JsonArray) JsonUtil.toJson(
+        "[                                 " +
+        "  { 'name': 'Duke',               " +
+        "    'age': 19,                    " +
+        "    'gender': 'M',                " +
+        "    'phones': {                   " +
+        "       'home': '650-123-4567',    " +
+        "       'mobile': '650-234-5678'}}," +
+        "  { 'name': 'Jane',               " +
+        "    'age': 24,                    " +
+        "    'gender': 'F',                " +
+        "    'phones': {                   " +
+        "       'mobile': '707-999-5555'}}," +
+        "  { 'name': 'Joanna',             " +
+        "    'gender': 'F',                " +
+        "    'phones': {                   " +
+        "       'mobile': '505-333-4444'}} " +
+        " ]");
+ 
+        assertEquals(expected, result);
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonDuplicateKeyTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonDuplicateKeyTest.java
new file mode 100644
index 0000000..c95d04a
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonDuplicateKeyTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.StringReader;
+import java.util.Collections;
+
+import org.glassfish.json.api.JsonConfig;
+import org.junit.Test;
+
+import jakarta.json.Json;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.stream.JsonParsingException;
+
+public class JsonDuplicateKeyTest {
+    @Test
+    public void testJsonReaderDuplicateKey1() {
+        String json = "{\"a\":\"b\",\"a\":\"c\"}";
+        JsonReader jsonReader = Json.createReader(new StringReader(json));
+        JsonObject jsonObject = jsonReader.readObject();
+        assertEquals(jsonObject.getString("a"), "c");
+    }
+
+    @Test
+    public void testJsonReaderDuplicateKey2() {
+        String json = "{\"a\":\"b\",\"a\":\"c\"}";
+        JsonReaderFactory jsonReaderFactory = Json.createReaderFactory(Collections.singletonMap(JsonConfig.REJECT_DUPLICATE_KEYS, true));
+        JsonReader jsonReader = jsonReaderFactory.createReader(new StringReader(json));
+        try {
+            jsonReader.readObject();
+            fail();
+        } catch (Exception e) {
+            assertTrue(e instanceof JsonParsingException);
+            assertEquals("Duplicate key 'a' is not allowed", e.getMessage());
+        }
+    }
+    
+    @Test
+    public void testJsonObjectBuilderDuplcateKey1() {
+    	JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+    	JsonObject jsonObject = objectBuilder.add("a", "b").add("a", "c").build();
+    	assertEquals(jsonObject.getString("a"), "c");
+    }
+    
+    @Test
+    public void testJsonObjectBuilderDuplcateKey2() {
+    	JsonBuilderFactory jsonBuilderFactory = Json.createBuilderFactory(Collections.singletonMap(JsonConfig.REJECT_DUPLICATE_KEYS, true));
+    	JsonObjectBuilder objectBuilder = jsonBuilderFactory.createObjectBuilder();
+    	try {
+    		objectBuilder.add("a", "b").add("a", "c").build();
+    		fail();
+    	} catch (Exception e) {
+            assertTrue(e instanceof IllegalStateException);
+            assertEquals("Duplicate key 'a' is not allowed", e.getMessage());
+    	}
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonFieldTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonFieldTest.java
new file mode 100644
index 0000000..4241573
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonFieldTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.Json;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerationException;
+import jakarta.json.stream.JsonGenerator;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.concurrent.Callable;
+
+/**
+ * Test for writing json field names without values.
+ *
+ * @author Roman Grigoriadi
+ */
+public class JsonFieldTest extends TestCase {
+
+    public void testFieldAsOnlyMember() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("fName");
+        generator.write("fValue");
+        generator.writeEnd();
+
+        generator.close();
+        assertEquals("{\"fName\":\"fValue\"}", sw.toString());
+    }
+
+    public void testFieldAsFirstMember() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("f1Name");
+        generator.write("f1Value");
+        generator.write("f2Name", "f2Value");
+        generator.writeEnd();
+
+        generator.close();
+        assertEquals("{\"f1Name\":\"f1Value\",\"f2Name\":\"f2Value\"}", sw.toString());
+    }
+
+    public void testFieldAsLastMember() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.write("f1Name", "f1Value");
+        generator.writeKey("f2Name");
+        generator.write("f2Value");
+        generator.writeEnd();
+
+        generator.close();
+        assertEquals("{\"f1Name\":\"f1Value\",\"f2Name\":\"f2Value\"}", sw.toString());
+    }
+
+
+    public void testFieldObject() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("f1Name");
+        generator.writeStartObject();
+        generator.write("innerFieldName", "innerFieldValue");
+        generator.writeEnd();
+        generator.write("f2Name", "f2Value");
+        generator.writeEnd();
+
+        generator.close();
+        assertEquals("{\"f1Name\":{\"innerFieldName\":\"innerFieldValue\"},\"f2Name\":\"f2Value\"}", sw.toString());
+    }
+
+    public void testFieldArray() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("f1Name");
+        generator.writeStartArray();
+        generator.write("arrayValue");
+        generator.writeEnd();
+        generator.write("f2Name", "f2Value");
+        generator.writeEnd();
+
+        generator.close();
+        assertEquals("{\"f1Name\":[\"arrayValue\"],\"f2Name\":\"f2Value\"}", sw.toString());
+    }
+
+    public void testFailFieldInField() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("f1Name");
+
+        try {
+            generator.write("f2Name", "f2Value");
+            fail("Field value, start object/array expected");
+        } catch (JsonGenerationException exception) {
+            //ok
+        }
+    }
+
+
+    public void testFailFieldKeyInArray() {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartArray();
+
+        try {
+            generator.writeKey("f1Value");
+            fail("Not allowed in array .");
+        } catch (JsonGenerationException exception) {
+            //ok
+        }
+    }
+
+    public void  testWriteString() {
+        assertEquals("{\"f1Name\":\"f1Value\"}", writeValue((gen)->gen.write("f1Value")));
+    }
+
+    public void  testWriteBigDec() {
+        assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(BigDecimal.TEN)));
+    }
+
+    public void  testWriteBigInt() {
+        assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(BigInteger.TEN)));
+    }
+
+    public void  testWriteBool() {
+        assertEquals("{\"f1Name\":true}", writeValue((gen)->gen.write(true)));
+    }
+
+    public void  testWriteInt() {
+        assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(10)));
+    }
+
+    public void  testWriteLong() {
+        assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(10L)));
+    }
+
+    public void  testWriteDouble() {
+        assertEquals("{\"f1Name\":10.0}", writeValue((gen)->gen.write(10d)));
+    }
+
+    public void  testWriteNull() {
+        assertEquals("{\"f1Name\":null}", writeValue(JsonGenerator::writeNull));
+    }
+
+    public void  testWriteJsonValue() {
+        JsonObjectBuilder builder = Json.createObjectBuilder();
+        builder.add("first", "value");
+        final JsonObject build = builder.build();
+        assertEquals("{\"f1Name\":\"value\"}", writeValue((gen)->gen.write(build.getValue("/first"))));
+    }
+
+    private String writeValue(WriteValueFunction writeValueCallback) {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+
+        generator.writeStartObject();
+        generator.writeKey("f1Name");
+        writeValueCallback.writeValue(generator);
+        generator.writeEnd();
+        generator.close();
+        return sw.toString();
+    }
+
+    private interface WriteValueFunction {
+        void writeValue(JsonGenerator generator);
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java
new file mode 100644
index 0000000..183725c
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonGeneratorFactory;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests JsonGeneratorFactory
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonGeneratorFactoryTest extends TestCase {
+
+    public JsonGeneratorFactoryTest(String testName) {
+        super(testName);
+    }
+
+    public void testGeneratorFactory() {
+        JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(null);
+
+        JsonGenerator generator1 = generatorFactory.createGenerator(new StringWriter());
+        generator1.writeStartArray().writeEnd();
+        generator1.close();
+
+        JsonGenerator generator2 = generatorFactory.createGenerator(new StringWriter());
+        generator2.writeStartArray().writeEnd();
+        generator2.close();
+    }
+
+    public void testGeneratorFactoryWithConfig() {
+        Map<String, Object> config = new HashMap<>();
+        config.put(JsonGenerator.PRETTY_PRINTING, true);
+        JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(config);
+        Map<String, ?> config1 = generatorFactory.getConfigInUse();
+        if (config1.size() != 1) {
+            throw new JsonException("Expecting no of properties=1, got="+config1.size());
+        }
+        assertTrue(config1.containsKey(JsonGenerator.PRETTY_PRINTING));
+
+        JsonGenerator generator1 = generatorFactory.createGenerator(new StringWriter());
+        generator1.writeStartArray().writeEnd();
+        generator1.close();
+
+        JsonGenerator generator2 = generatorFactory.createGenerator(new StringWriter());
+        generator2.writeStartArray().writeEnd();
+        generator2.close();
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java
new file mode 100644
index 0000000..43745e0
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+import org.glassfish.json.api.BufferPool;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * {@link JsonGenerator} tests
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonGeneratorTest extends TestCase {
+    public JsonGeneratorTest(String testName) {
+        super(testName);
+    }
+
+    public void testObjectWriter() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        testObject(generator);
+        generator.close();
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(writer.toString()));
+        JsonObject person = reader.readObject();
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testObjectStream() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonGenerator generator = Json.createGenerator(out);
+        testObject(generator);
+        generator.close();
+        out.close();
+
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        JsonReader reader = Json.createReader(in);
+        JsonObject person = reader.readObject();
+        JsonObjectTest.testPerson(person);
+        reader.close();
+        in.close();
+    }
+
+    static void testObject(JsonGenerator generator) throws Exception {
+        generator
+                .writeStartObject()
+                .write("firstName", "John")
+                .write("lastName", "Smith")
+                .write("age", 25)
+                .writeStartObject("address")
+                .write("streetAddress", "21 2nd Street")
+                .write("city", "New York")
+                .write("state", "NY")
+                .write("postalCode", "10021")
+                .writeEnd()
+                .writeStartArray("phoneNumber")
+                .writeStartObject()
+                .write("type", "home")
+                .write("number", "212 555-1234")
+                .writeEnd()
+                .writeStartObject()
+                .write("type", "fax")
+                .write("number", "646 555-4567")
+                .writeEnd()
+                .writeEnd()
+                .writeEnd();
+    }
+
+    public void testArray() throws Exception {
+        Writer sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+        generator
+                .writeStartArray()
+                .writeStartObject()
+                .write("type", "home")
+                .write("number", "212 555-1234")
+                .writeEnd()
+                .writeStartObject()
+                .write("type", "fax")
+                .write("number", "646 555-4567")
+                .writeEnd()
+                .writeEnd();
+        generator.close();
+    }
+
+    // tests JsonGenerator when JsonValue is used for generation
+    public void testJsonValue() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator
+                .writeStartObject()
+                .write("firstName", "John")
+                .write("lastName", "Smith")
+                .write("age", 25)
+                .write("address", JsonBuilderTest.buildAddress())
+                .write("phoneNumber", JsonBuilderTest.buildPhone())
+                .writeEnd();
+        generator.close();
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(writer.toString()));
+        JsonObject person = reader.readObject();
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testArrayString() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartArray().write("string").writeEnd();
+        generator.close();
+        writer.close();
+
+        assertEquals("[\"string\"]", writer.toString());
+    }
+
+    public void testEscapedString() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartArray().write("\u0000").writeEnd();
+        generator.close();
+        writer.close();
+
+        assertEquals("[\"\\u0000\"]", writer.toString());
+    }
+
+    public void testEscapedString1() throws Exception {
+        String expected = "\u0000\u00ff";
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+        generator.writeStartArray().write("\u0000\u00ff").writeEnd();
+        generator.close();
+        sw.close();
+
+        JsonReader jr = Json.createReader(new StringReader(sw.toString()));
+        JsonArray array = jr.readArray();
+        String got = array.getString(0);
+        jr.close();
+
+        assertEquals(expected, got);
+    }
+
+    public void testGeneratorEquals() throws Exception {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+        generator.writeStartArray()
+                .write(JsonValue.TRUE)
+                .write(JsonValue.FALSE)
+                .write(JsonValue.NULL)
+                .write(Integer.MAX_VALUE)
+                .write(Long.MAX_VALUE)
+                .write(Double.MAX_VALUE)
+                .write(Integer.MIN_VALUE)
+                .write(Long.MIN_VALUE)
+                .write(Double.MIN_VALUE)
+                .writeEnd();
+        generator.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonArray expected = reader.readArray();
+        reader.close();
+
+        JsonArray actual = Json.createArrayBuilder()
+                .add(JsonValue.TRUE)
+                .add(JsonValue.FALSE)
+                .add(JsonValue.NULL)
+                .add(Integer.MAX_VALUE)
+                .add(Long.MAX_VALUE)
+                .add(Double.MAX_VALUE)
+                .add(Integer.MIN_VALUE)
+                .add(Long.MIN_VALUE)
+                .add(Double.MIN_VALUE)
+                .build();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testPrettyObjectWriter() throws Exception {
+        StringWriter writer = new StringWriter();
+        Map<String, Object> config = new HashMap<>();
+        config.put(JsonGenerator.PRETTY_PRINTING, true);
+        JsonGenerator generator = Json.createGeneratorFactory(config)
+                .createGenerator(writer);
+        testObject(generator);
+        generator.close();
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(writer.toString()));
+        JsonObject person = reader.readObject();
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testPrettyPrinting() throws Exception {
+        String[][] lines = {{"firstName", "John"}, {"lastName", "Smith"}};
+        StringWriter writer = new StringWriter();
+        Map<String, Object> config = new HashMap<>();
+        config.put(JsonGenerator.PRETTY_PRINTING, true);
+        JsonGenerator generator = Json.createGeneratorFactory(config)
+                .createGenerator(writer);
+        generator.writeStartObject()
+                .write("firstName", "John")
+                .write("lastName", "Smith")
+                .writeEnd();
+        generator.close();
+        writer.close();
+
+        BufferedReader reader = new BufferedReader(new StringReader(writer.toString()));
+        int numberOfLines = 0;
+        String line;
+        while ((line = reader.readLine()) != null) {
+            numberOfLines++;
+            if (numberOfLines > 1 && numberOfLines < 4) {
+                assertTrue(line.contains("\"" + lines[numberOfLines - 2][0] + "\": \"" + lines[numberOfLines - 2][1] + "\""));
+            }
+        }
+        assertEquals(4, numberOfLines);
+    }
+
+    public void testPrettyObjectStream() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        Map<String, Object> config = new HashMap<>();
+        config.put(JsonGenerator.PRETTY_PRINTING, true);
+        JsonGenerator generator = Json.createGeneratorFactory(config)
+                .createGenerator(out);
+        testObject(generator);
+        generator.close();
+        out.close();
+
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        JsonReader reader = Json.createReader(in);
+        JsonObject person = reader.readObject();
+        JsonObjectTest.testPerson(person);
+        reader.close();
+        in.close();
+    }
+
+    public void testGenerationException1() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartObject();
+        try {
+            generator.writeStartObject();
+            fail("Expected JsonGenerationException, writeStartObject() cannot be called more than once");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException2() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartObject();
+        try {
+            generator.writeStartArray();
+            fail("Expected JsonGenerationException, writeStartArray() is valid in no context");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException3() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        try {
+            generator.close();
+            fail("Expected JsonGenerationException, no JSON is generated");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException4() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartArray();
+        try {
+            generator.close();
+            fail("Expected JsonGenerationException, writeEnd() is not called");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException5() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartObject();
+        try {
+            generator.close();
+            fail("Expected JsonGenerationException, writeEnd() is not called");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException6() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartObject().writeEnd();
+        try {
+            generator.writeStartObject();
+            fail("Expected JsonGenerationException, cannot generate one more JSON text");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException7() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartArray().writeEnd();
+        try {
+            generator.writeStartArray();
+            fail("Expected JsonGenerationException, cannot generate one more JSON text");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+
+    public void testGenerationException8() throws Exception {
+        StringWriter sWriter = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sWriter);
+        generator.writeStartObject();
+        try {
+            generator.write(JsonValue.TRUE);
+            fail("Expected JsonGenerationException, cannot generate one more JSON text");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGenerationException9() throws Exception {
+        StringWriter sWriter = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sWriter);
+        generator.writeStartObject();
+        try {
+            generator.write("name");
+            fail("Expected JsonGenerationException, cannot generate one more JSON text");
+        } catch (JsonGenerationException je) {
+            // Expected exception
+        }
+    }
+
+    public void testGeneratorArrayDouble() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartArray();
+        try {
+            generator.write(Double.NaN);
+            fail("JsonGenerator.write(Double.NaN) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        try {
+            generator.write(Double.POSITIVE_INFINITY);
+            fail("JsonGenerator.write(Double.POSITIVE_INIFINITY) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        try {
+            generator.write(Double.NEGATIVE_INFINITY);
+            fail("JsonGenerator.write(Double.NEGATIVE_INIFINITY) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        generator.writeEnd();
+        generator.close();
+    }
+
+    public void testGeneratorObjectDouble() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(writer);
+        generator.writeStartObject();
+        try {
+            generator.write("foo", Double.NaN);
+            fail("JsonGenerator.write(String, Double.NaN) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        try {
+            generator.write("foo", Double.POSITIVE_INFINITY);
+            fail("JsonGenerator.write(String, Double.POSITIVE_INIFINITY) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        try {
+            generator.write("foo", Double.NEGATIVE_INFINITY);
+            fail("JsonGenerator.write(String, Double.NEGATIVE_INIFINITY) should produce NumberFormatException");
+        } catch (NumberFormatException ne) {
+            // expected
+        }
+        generator.writeEnd();
+        generator.close();
+    }
+
+    public void testIntGenerator() throws Exception {
+        Random r = new Random(System.currentTimeMillis());
+        JsonGeneratorFactory gf = Json.createGeneratorFactory(null);
+        JsonReaderFactory rf = Json.createReaderFactory(null);
+        JsonBuilderFactory bf = Json.createBuilderFactory(null);
+        for(int i=0; i < 100000; i++) {
+            int num = r.nextInt();
+            StringWriter sw = new StringWriter();
+            JsonGenerator generator = gf.createGenerator(sw);
+            generator.writeStartArray().write(num).writeEnd().close();
+
+            JsonReader reader = rf.createReader(new StringReader(sw.toString()));
+            JsonArray got = reader.readArray();
+            reader.close();
+
+            JsonArray expected = bf.createArrayBuilder().add(num).build();
+
+            assertEquals(expected, got);
+        }
+    }
+
+    public void testGeneratorBuf() throws Exception {
+        JsonGeneratorFactory gf = Json.createGeneratorFactory(null);
+        JsonReaderFactory rf = Json.createReaderFactory(null);
+        JsonBuilderFactory bf = Json.createBuilderFactory(null);
+        StringBuilder sb = new StringBuilder();
+        int value = 10;
+        for(int i=0; i < 25000; i++) {
+            sb.append('a');
+            String name = sb.toString();
+            StringWriter sw = new StringWriter();
+            JsonGenerator generator = gf.createGenerator(sw);
+            generator.writeStartObject().write(name, value).writeEnd().close();
+
+            JsonReader reader = rf.createReader(new StringReader(sw.toString()));
+            JsonObject got = reader.readObject();
+            reader.close();
+
+            JsonObject expected = bf.createObjectBuilder().add(name, value).build();
+
+            assertEquals(expected, got);
+        }
+    }
+
+    public void testBufferPoolFeature() {
+        final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(1024);
+        Map<String, Object> config = new HashMap<String, Object>() {{
+            put(BufferPool.class.getName(), bufferPool);
+        }};
+
+        JsonGeneratorFactory factory = Json.createGeneratorFactory(config);
+        JsonGenerator generator = factory.createGenerator(new StringWriter());
+        generator.writeStartArray();
+        generator.writeEnd();
+        generator.close();
+        assertTrue(bufferPool.isTakeCalled());
+        assertTrue(bufferPool.isRecycleCalled());
+    }
+
+    public void testBufferSizes() {
+        JsonReaderFactory rf = Json.createReaderFactory(null);
+        JsonBuilderFactory bf = Json.createBuilderFactory(null);
+        for(int size=10; size < 1000; size++) {
+            final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(size);
+            Map<String, Object> config = new HashMap<String, Object>() {{
+                put(BufferPool.class.getName(), bufferPool);
+            }};
+            JsonGeneratorFactory gf = Json.createGeneratorFactory(config);
+
+            StringBuilder sb = new StringBuilder();
+            int value = 10;
+            for(int i=0; i < 1500; i++) {
+                sb.append('a');
+                String name = sb.toString();
+                StringWriter sw = new StringWriter();
+                JsonGenerator generator = gf.createGenerator(sw);
+                generator.writeStartObject().write(name, value).writeEnd().close();
+
+                JsonReader reader = rf.createReader(new StringReader(sw.toString()));
+                JsonObject got = reader.readObject();
+                reader.close();
+
+                JsonObject expected = bf.createObjectBuilder().add(name, value).build();
+
+                assertEquals(expected, got);
+            }
+
+        }
+    }
+
+    public void testString() throws Exception {
+        escapedString("");
+        escapedString("abc");
+        escapedString("abc\f");
+        escapedString("abc\na");
+        escapedString("abc\tabc");
+        escapedString("abc\n\tabc");
+        escapedString("abc\n\tabc\r");
+        escapedString("\n\tabc\r");
+        escapedString("\bab\tb\rc\\\"\ftesting1234");
+        escapedString("\f\babcdef\tb\rc\\\"\ftesting1234");
+    }
+
+    void escapedString(String expected) throws Exception {
+        StringWriter sw = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(sw);
+        generator.writeStartArray().write(expected).writeEnd();
+        generator.close();
+        sw.close();
+
+        JsonReader jr = Json.createReader(new StringReader(sw.toString()));
+        JsonArray array = jr.readArray();
+        String got = array.getString(0);
+        jr.close();
+
+        assertEquals(expected, got);
+    }
+
+    public void testFlush() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        JsonGenerator gen = Json.createGenerator(baos);
+        gen.writeStartObject().writeEnd();
+        gen.flush();
+
+        assertEquals("{}", baos.toString("UTF-8"));
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java
new file mode 100644
index 0000000..24f8f8b
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonMergePatchDiffTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        List<Object[]> examples = new ArrayList<Object[]>();
+        JsonArray data = loadData();
+        for (JsonValue jsonValue : data) {
+            JsonObject test = (JsonObject) jsonValue;
+            Object[] testData = new Object[4];
+            testData[0] = test.get("original");
+            testData[1] = test.get("target");
+            testData[2] = test.get("expected");
+            testData[3] = createExceptionClass((JsonString)test.get("exception"));
+
+            examples.add(testData);
+        }
+
+        return examples;
+    }
+
+    private static Class<? extends Exception> createExceptionClass(
+            JsonString exceptionClassName) throws ClassNotFoundException {
+        if (exceptionClassName != null) {
+            return (Class<? extends Exception>) Class
+                    .forName(exceptionClassName.getString());
+        }
+        return null;
+    }
+
+    private static JsonArray loadData() {
+        InputStream testData = JsonPatchTest.class
+                .getResourceAsStream("/jsonmergepatchdiff.json");
+        JsonReader reader = Json.createReader(testData);
+        JsonArray data = (JsonArray) reader.read();
+        return data;
+    }
+
+    private JsonValue original;
+    private JsonValue target;
+    private JsonValue expected;
+    private Class<? extends Exception> expectedException;
+
+    public JsonMergePatchDiffTest(JsonValue original, JsonValue target,
+            JsonValue expected, Class<? extends Exception> expectedException) {
+        super();
+        this.original = original;
+        this.target = target;
+        this.expected = expected;
+        this.expectedException = expectedException;
+    }
+    @Test
+    public void shouldExecuteJsonMergePatchDiffOperationsToJsonDocument() {
+        try {
+            JsonValue output = Json.createMergeDiff(original, target).toJsonValue();
+            assertThat(output, is(expected));
+            assertThat(expectedException, nullValue());
+        } catch (Exception e) {
+            if (expectedException == null) {
+                fail(e.getMessage());
+            } else {
+                assertThat(e, instanceOf(expectedException));
+            }
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java
new file mode 100644
index 0000000..d839140
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonMergePatchTest {
+
+        @Parameters(name = "{index}: ({0})={1}")
+        public static Iterable<Object[]> data() throws Exception {
+            List<Object[]> examples = new ArrayList<Object[]>();
+            JsonArray data = loadData();
+            for (JsonValue jsonValue : data) {
+                JsonObject test = (JsonObject) jsonValue;
+                Object[] testData = new Object[4];
+                testData[0] = test.get("patch");
+                testData[1] = test.get("target");
+                testData[2] = test.get("expected");
+                testData[3] = createExceptionClass((JsonString)test.get("exception"));
+
+                examples.add(testData);
+            }
+
+            return examples;
+        }
+
+        private static Class<? extends Exception> createExceptionClass(
+                JsonString exceptionClassName) throws ClassNotFoundException {
+            if (exceptionClassName != null) {
+                return (Class<? extends Exception>) Class
+                        .forName(exceptionClassName.getString());
+            }
+            return null;
+        }
+
+        private static JsonArray loadData() {
+            InputStream testData = JsonPatchTest.class
+                    .getResourceAsStream("/jsonmergepatch.json");
+            JsonReader reader = Json.createReader(testData);
+            JsonArray data = (JsonArray) reader.read();
+            return data;
+        }
+
+        private JsonValue patch;
+        private JsonValue target;
+        private JsonValue expected;
+        private Class<? extends Exception> expectedException;
+
+        public JsonMergePatchTest(JsonValue patch, JsonValue target,
+                JsonValue expected, Class<? extends Exception> expectedException) {
+            super();
+            this.patch = patch;
+            this.target = target;
+            this.expected = expected;
+            this.expectedException = expectedException;
+        }
+
+        @Test
+        public void shouldExecuteJsonMergePatchDiffOperationsToJsonDocument() {
+            try {
+                JsonValue output = Json.createMergePatch(patch).apply(target);
+                assertThat(output, is(expected));
+                assertThat(expectedException, nullValue());
+            } catch (Exception e) {
+                if (expectedException == null) {
+                    fail(e.getMessage());
+                } else {
+                    assertThat(e, instanceOf(expectedException));
+                }
+            }
+        }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonNumberTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonNumberTest.java
new file mode 100644
index 0000000..e830dbb
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonNumberTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonNumberTest extends TestCase {
+    public JsonNumberTest(String testName) {
+        super(testName);
+    }
+
+    public void testFloating() throws Exception {
+        JsonArray array1 = Json.createArrayBuilder().add(10.4).build();
+        JsonReader reader = Json.createReader(new StringReader("[10.4]"));
+        JsonArray array2 = reader.readArray();
+
+        assertEquals(array1.get(0), array2.get(0));
+        assertEquals(array1, array2);
+    }
+
+    public void testBigDecimal() throws Exception {
+        JsonArray array1 = Json.createArrayBuilder().add(new BigDecimal("10.4")).build();
+        JsonReader reader = Json.createReader(new StringReader("[10.4]"));
+        JsonArray array2 = reader.readArray();
+
+        assertEquals(array1.get(0), array2.get(0));
+        assertEquals(array1, array2);
+    }
+
+    public void testIntNumberType() throws Exception {
+        JsonArray array1 = Json.createArrayBuilder()
+                .add(Integer.MIN_VALUE)
+                .add(Integer.MAX_VALUE)
+                .add(Integer.MIN_VALUE + 1)
+                .add(Integer.MAX_VALUE - 1)
+                .add(12)
+                .add(12l)
+                .add(new BigInteger("0"))
+                .build();
+        testNumberType(array1, true);
+
+        StringReader sr = new StringReader("[" +
+                "-2147483648, " +
+                "2147483647, " +
+                "-2147483647, " +
+                "2147483646, " +
+                "12, " +
+                "12, " +
+                "0 " +
+                "]");
+        JsonReader reader = Json.createReader(sr);
+        JsonArray array2 = reader.readArray();
+        reader.close();
+        testNumberType(array2, true);
+
+        assertEquals(array1, array2);
+    }
+
+    private void testNumberType(JsonArray array, boolean integral) {
+        for (JsonValue value : array) {
+            assertEquals(integral, ((JsonNumber) value).isIntegral());
+        }
+    }
+
+    public void testLongNumberType() throws Exception {
+        JsonArray array1 = Json.createArrayBuilder()
+                .add(Long.MIN_VALUE)
+                .add(Long.MAX_VALUE)
+                .add(Long.MIN_VALUE + 1)
+                .add(Long.MAX_VALUE - 1)
+                .add((long) Integer.MIN_VALUE - 1)
+                .add((long) Integer.MAX_VALUE + 1)
+                .build();
+        testNumberType(array1, true);
+
+        StringReader sr = new StringReader("[" +
+                "-9223372036854775808, " +
+                "9223372036854775807, " +
+                "-9223372036854775807, " +
+                "9223372036854775806, " +
+                "-2147483649, " +
+                "2147483648 " +
+                "]");
+        JsonReader reader = Json.createReader(sr);
+        JsonArray array2 = reader.readArray();
+        reader.close();
+        testNumberType(array2, true);
+
+        assertEquals(array1, array2);
+    }
+
+
+//    public void testBigIntegerNumberType() throws Exception {
+//        JsonArray array1 = new JsonBuilder()
+//            .startArray()
+//                .add(new BigInteger("-9223372036854775809"))
+//                .add(new BigInteger("9223372036854775808"))
+//                .add(new BigInteger("012345678901234567890"))
+//            .end()
+//        .build();
+//        testNumberType(array1, JsonNumber.NumberType.BIG_INTEGER);
+//
+//        StringReader sr = new StringReader("[" +
+//            "-9223372036854775809, " +
+//            "9223372036854775808, " +
+//            "12345678901234567890 " +
+//        "]");
+//        JsonReader reader = new JsonReader(sr);
+//        JsonArray array2 = reader.readArray();
+//        reader.close();
+//        testNumberType(array2, JsonNumber.NumberType.BIG_INTEGER);
+//
+//        assertEquals(array1, array2);
+//    }
+
+    public void testBigDecimalNumberType() throws Exception {
+        JsonArray array1 = Json.createArrayBuilder()
+                .add(12d)
+                .add(12.0d)
+                .add(12.1d)
+                .add(Double.MIN_VALUE)
+                .add(Double.MAX_VALUE)
+                .build();
+        testNumberType(array1, false);
+
+        StringReader sr = new StringReader("[" +
+                "12.0, " +
+                "12.0, " +
+                "12.1, " +
+                "4.9E-324, " +
+                "1.7976931348623157E+308 " +
+                "]");
+        JsonReader reader = Json.createReader(sr);
+        JsonArray array2 = reader.readArray();
+        reader.close();
+        testNumberType(array2, false);
+
+        assertEquals(array1, array2);
+    }
+
+    public void testMinMax() throws Exception {
+        JsonArray expected = Json.createArrayBuilder()
+                .add(Integer.MIN_VALUE)
+                .add(Integer.MAX_VALUE)
+                .add(Long.MIN_VALUE)
+                .add(Long.MAX_VALUE)
+                .add(Double.MIN_VALUE)
+                .add(Double.MAX_VALUE)
+                .build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.writeArray(expected);
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonArray actual = reader.readArray();
+        reader.close();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testLeadingZeroes() {
+        JsonArray array = Json.createArrayBuilder()
+                .add(0012.1d)
+                .build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter jw = Json.createWriter(sw);
+        jw.write(array);
+        jw.close();
+
+        assertEquals("[12.1]", sw.toString());
+    }
+
+    public void testBigIntegerExact() {
+        try {
+            JsonArray array = Json.createArrayBuilder().add(12345.12345).build();
+            array.getJsonNumber(0).bigIntegerValueExact();
+            fail("Expected Arithmetic exception");
+        } catch (ArithmeticException expected) {
+            // no-op
+        }
+    }
+
+    public void testHashCode() {
+        JsonNumber jsonNumber1 = Json.createValue(1);
+        assertTrue(jsonNumber1.hashCode() == jsonNumber1.bigDecimalValue().hashCode());
+
+        JsonNumber jsonNumber2 = Json.createValue(1);
+
+        assertTrue(jsonNumber1.equals(jsonNumber2));
+        assertTrue(jsonNumber1.hashCode() == jsonNumber2.hashCode());
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonObjectTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonObjectTest.java
new file mode 100644
index 0000000..a0ac06c
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonObjectTest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.json.*;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonObjectTest extends TestCase {
+    public JsonObjectTest(String testName) {
+        super(testName);
+    }
+
+    public void test() {
+    }
+
+    public void testEmptyObjectEquals() throws Exception {
+        JsonObject empty1 = Json.createObjectBuilder()
+                .build();
+
+        JsonObject empty2 = Json.createObjectBuilder()
+                .build();
+
+        assertEquals(empty1, empty2);
+    }
+
+    public void testPersonObjectEquals() throws Exception {
+        JsonObject person1 = JsonBuilderTest.buildPerson();
+        JsonObject person2 = JsonReaderTest.readPerson();
+
+        assertEquals(person1, person2);
+    }
+
+    public void testGetStringOrDefault() throws Exception {
+        JsonObject object = Json.createObjectBuilder()
+                .add("string", "value")
+                .add("number", 25)
+                .add("boolean", false)
+                .build();
+        assertEquals("value", object.getString("string", "default"));
+        assertEquals("default", object.getString("missing", "default"));
+        assertEquals("default", object.getString("number", "default"));
+    }
+
+    public void testGetIntOrDefault() throws Exception {
+        JsonObject object = Json.createObjectBuilder()
+                .add("string", "value")
+                .add("number", 25)
+                .add("boolean", false)
+                .build();
+        assertEquals(25, object.getInt("number", 10));
+        assertEquals(10, object.getInt("missing", 10));
+        assertEquals(10, object.getInt("string", 10));
+    }
+
+    public void testGetBooleanOrDefault() throws Exception {
+        JsonObject object = Json.createObjectBuilder()
+                .add("string", "value")
+                .add("number", 25)
+                .add("boolean", false)
+                .build();
+        assertFalse(object.getBoolean("boolean", true));
+        assertTrue(object.getBoolean("missing", true));
+        assertTrue(object.getBoolean("string", true));
+    }
+
+    static void testPerson(JsonObject person) {
+        assertEquals(5, person.size());
+        assertEquals("John", person.getString("firstName"));
+        assertEquals("Smith", person.getString("lastName"));
+        assertEquals(25, person.getJsonNumber("age").intValue());
+        assertEquals(25, person.getInt("age"));
+
+        JsonObject address = person.getJsonObject("address");
+        assertEquals(4, address.size());
+        assertEquals("21 2nd Street", address.getString("streetAddress"));
+        assertEquals("New York", address.getString("city"));
+        assertEquals("NY", address.getString("state"));
+        assertEquals("10021", address.getString("postalCode"));
+
+        JsonArray phoneNumber = person.getJsonArray("phoneNumber");
+        assertEquals(2, phoneNumber.size());
+        JsonObject home = phoneNumber.getJsonObject(0);
+        assertEquals(2, home.size());
+        assertEquals("home", home.getString("type"));
+        assertEquals("212 555-1234", home.getString("number"));
+        assertEquals("212 555-1234", home.getString("number"));
+
+        JsonObject fax = phoneNumber.getJsonObject(1);
+        assertEquals(2, fax.size());
+        assertEquals("fax", fax.getString("type"));
+        assertEquals("646 555-4567", fax.getString("number"));
+
+        assertEquals("\"646 555-4567\"", fax.getJsonString("number").toString());
+    }
+
+    static void testEmpty(JsonObject empty) {
+        assertTrue(empty.isEmpty());
+    }
+
+    public void testClassCastException() {
+        JsonObject obj = Json.createObjectBuilder()
+                .add("foo", JsonValue.FALSE).build();
+        try {
+            obj.getJsonNumber("foo");
+            fail("Expected ClassCastException for casting JsonValue.FALSE to JsonNumber");
+        } catch (ClassCastException ce) {
+            // Expected
+        }
+    }
+
+    public void testPut() {
+        JsonObject obj = Json.createObjectBuilder().add("foo", 1).build();
+        try {
+            obj.put("bar", JsonValue.FALSE);
+            fail("JsonObject#put() should throw UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    public void testRemove() {
+        JsonObject obj = Json.createObjectBuilder().add("foo", 1).build();
+        try {
+            obj.remove("foo");
+            fail("JsonObject#remove() should throw UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    public void testObjectBuilderWithVariousValues() {
+        JsonObject expected = Json.createObjectBuilder()
+                .add("a", JsonValue.TRUE)
+                .add("b", JsonValue.FALSE)
+                .add("c", JsonValue.NULL)
+                .add("d", Integer.MAX_VALUE)
+                .add("e", Long.MAX_VALUE)
+                .add("f", Double.MAX_VALUE)
+                .add("g", Integer.MIN_VALUE)
+                .add("h", Long.MIN_VALUE)
+                .add("i", Double.MIN_VALUE)
+                .add("j", Json.createArrayBuilder().add("abc"))
+                .add("k", Json.createObjectBuilder().add("one", 1))
+                .build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.writeObject(expected);
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonObject actual = reader.readObject();
+        reader.close();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testObjectBuilderWithMap() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("a", JsonValue.TRUE);
+        map.put("b", JsonValue.FALSE);
+        map.put("c", JsonValue.NULL);
+        map.put("d", Integer.MAX_VALUE);
+        map.put("e", Long.MAX_VALUE);
+        map.put("f", Double.MAX_VALUE);
+        map.put("g", Integer.MIN_VALUE);
+        map.put("h", Long.MIN_VALUE);
+        map.put("i", Double.MIN_VALUE);
+        map.put("j", Json.createArrayBuilder().add("abc"));
+        map.put("k", Json.createObjectBuilder().add("one", 1));
+
+        JsonObject expected = Json.createObjectBuilder(map).build();
+
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.writeObject(expected);
+        writer.close();
+
+        JsonReader reader = Json.createReader(new StringReader(sw.toString()));
+        JsonObject actual = reader.readObject();
+        reader.close();
+
+        assertEquals(expected, actual);
+    }
+
+    public void testObjectBuilderNpe() {
+        try {
+            JsonObject obj = Json.createObjectBuilder().add(null, 1).build();
+            fail("JsonObjectBuilder#add(null, 1) should throw NullPointerException");
+        } catch(NullPointerException e) {
+            // Expected
+        }
+    }
+
+    public void testHashCode() {
+        JsonObject object1 = Json.createObjectBuilder().add("a", 1).add("b", 2).add("c", 3).build();
+        assertTrue(object1.hashCode() == object1.hashCode()); //1st call compute hashCode, 2nd call returns cached value
+
+        JsonObject object2 = Json.createObjectBuilder().add("a", 1).add("b", 2).add("c", 3).build();
+        assertTrue(object1.hashCode() == object2.hashCode());
+
+        JsonObject object3 = Json.createObjectBuilder().build(); //org.glassfish.json.JsonArrayBuilderImpl.JsonArrayImpl
+        JsonObject object4 = JsonValue.EMPTY_JSON_OBJECT; //jakarta.json.EmptyObject
+
+        assertTrue(object3.equals(object4));
+        assertTrue(object3.hashCode() == object4.hashCode()); //equal instances have same hashCode
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java
new file mode 100644
index 0000000..c222cdb
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParserFactory;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests JsonParserFactory
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonParserFactoryTest extends TestCase {
+
+    public JsonParserFactoryTest(String testName) {
+        super(testName);
+    }
+
+    public void testParserFactory() {
+        JsonParserFactory parserFactory = Json.createParserFactory(null);
+        JsonParser parser1 = parserFactory.createParser(new StringReader("[]"));
+        parser1.close();
+        JsonParser parser2 = parserFactory.createParser(new StringReader("[]"));
+        parser2.close();
+    }
+
+    public void testParserFactoryWithConfig() {
+        Map<String, ?> config = new HashMap<>();
+        JsonParserFactory parserFactory = Json.createParserFactory(config);
+        JsonParser parser1 = parserFactory.createParser(new StringReader("[]"));
+        parser1.close();
+        JsonParser parser2 = parserFactory.createParser(new StringReader("[]"));
+        parser2.close();
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java
new file mode 100644
index 0000000..72a17fd
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import java.io.StringReader;
+import jakarta.json.Json;
+import jakarta.json.stream.JsonParser;
+import junit.framework.TestCase;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+/**
+ *
+ * @author lukas
+ */
+public class JsonParserSkipTest extends TestCase {
+
+    public void testSkipArrayReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("[[],[[]]]"))) {
+            testSkipArray(parser);
+        }
+    }
+
+    public void testSkipArrayStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder()
+                        .add(Json.createArrayBuilder())
+                        .add(Json.createArrayBuilder()
+                                .add(Json.createArrayBuilder()))
+                        .build())) {
+            testSkipArray(parser);
+        }
+    }
+
+    private static void testSkipArray(JsonParser parser) {
+        assertEquals(JsonParser.Event.START_ARRAY, parser.next());
+        parser.skipArray();
+        assertEquals(false, parser.hasNext());
+    }
+
+    public void testSkipArrayInObjectReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{\"array\":[[],[[]]],\"object\":\"value2\"}"))) {
+            testSkipArrayInObject(parser);
+        }
+    }
+
+    public void testSkipArrayInObjectStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder().add("array", Json.createArrayBuilder()
+                        .add(Json.createArrayBuilder())
+                        .add(Json.createArrayBuilder()
+                                .add(Json.createArrayBuilder()))
+                ).add("object", "value2")
+                        .build())) {
+            testSkipArrayInObject(parser);
+        }
+    }
+
+    private static void testSkipArrayInObject(JsonParser parser) {
+        assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+        assertEquals(JsonParser.Event.KEY_NAME, parser.next());
+        assertEquals(JsonParser.Event.START_ARRAY, parser.next());
+        parser.skipArray();
+        assertTrue(parser.hasNext());
+        assertEquals(JsonParser.Event.KEY_NAME, parser.next());
+        assertEquals(JsonParser.Event.VALUE_STRING, parser.next());
+        assertEquals(JsonParser.Event.END_OBJECT, parser.next());
+        assertFalse(parser.hasNext());
+    }
+
+    public void testSkipObjectReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{\"array\":[],\"objectToSkip\":{\"huge key\":\"huge value\"},\"simple\":2}"))) {
+            testSkipObject(parser);
+        }
+    }
+
+    public void testSkipObjectStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder()
+                        .add("array", Json.createArrayBuilder().build())
+                        .add("objectToSkip", Json.createObjectBuilder().add("huge key", "huge value"))
+                        .add("simple", 2)
+                        .build())) {
+            testSkipObject(parser);
+        }
+    }
+
+    private static void testSkipObject(JsonParser parser) {
+        assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+        assertEquals(JsonParser.Event.KEY_NAME, parser.next());
+        assertEquals(JsonParser.Event.START_ARRAY, parser.next());
+        assertEquals(JsonParser.Event.END_ARRAY, parser.next());
+        assertEquals(JsonParser.Event.KEY_NAME, parser.next());
+        assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+        parser.skipObject();
+        assertEquals(JsonParser.Event.KEY_NAME, parser.next());
+        assertEquals(JsonParser.Event.VALUE_NUMBER, parser.next());
+        assertEquals(JsonParser.Event.END_OBJECT, parser.next());
+        assertEquals(false, parser.hasNext());
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonParserTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonParserTest.java
new file mode 100644
index 0000000..ed5d6c9
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonParserTest.java
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import jakarta.json.stream.JsonParserFactory;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Scanner;
+import jakarta.json.stream.JsonParsingException;
+
+import org.glassfish.json.api.BufferPool;
+
+/**
+ * JsonParser Tests
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonParserTest extends TestCase {
+    static final Charset UTF_32LE = Charset.forName("UTF-32LE");
+    static final Charset UTF_32BE = Charset.forName("UTF-32BE");
+
+    public JsonParserTest(String testName) {
+        super(testName);
+    }
+
+    public void testReader() {
+        JsonParser reader = Json.createParser(
+                new StringReader("{ \"a\" : \"b\", \"c\" : null, \"d\" : [null, \"abc\"] }"));
+        reader.close();
+    }
+
+
+    public void testEmptyArrayReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("[]"))) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStream() {
+        try (JsonParser parser = Json.createParser(
+                new ByteArrayInputStream(new byte[]{'[', ']'}))) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF8() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF16LE() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16LE));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF16BE() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16BE));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF32LE() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(UTF_32LE));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF32BE() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(UTF_32BE));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamUTF16() {
+        ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStreamWithConfig() {
+        Map<String, ?> config = new HashMap<>();
+        try (JsonParser parser = Json.createParserFactory(config).createParser(
+                new ByteArrayInputStream(new byte[]{'[', ']'}))) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder().build())) {
+            testEmptyArray(parser);
+        }
+    }
+
+    public void testEmptyArrayStructureWithConfig() {
+        Map<String, ?> config = new HashMap<>();
+        try (JsonParser parser = Json.createParserFactory(config).createParser(
+                Json.createArrayBuilder().build())) {
+            testEmptyArray(parser);
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static void testEmptyArray(JsonParser parser) {
+        while (parser.hasNext()) {
+            parser.next();
+        }
+    }
+
+
+    public void testEmptyArrayReaderIterator() {
+        try (JsonParser parser = Json.createParser(new StringReader("[]"))) {
+            testEmptyArrayIterator(parser);
+        }
+    }
+
+    public void testEmptyArrayStructureIterator() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder().build())) {
+            testEmptyArrayIterator(parser);
+        }
+    }
+
+    static void testEmptyArrayIterator(JsonParser parser) {
+        assertEquals(true, parser.hasNext());
+        assertEquals(true, parser.hasNext());
+        assertEquals(Event.START_ARRAY, parser.next());
+
+        assertEquals(true, parser.hasNext());
+        assertEquals(true, parser.hasNext());
+        assertEquals(Event.END_ARRAY, parser.next());
+
+        assertEquals(false, parser.hasNext());
+        assertEquals(false, parser.hasNext());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+        }
+    }
+
+
+    public void testEmptyArrayIterator2Reader() {
+        try (JsonParser parser = Json.createParser(new StringReader("[]"))) {
+            testEmptyArrayIterator2(parser);
+        }
+    }
+
+    public void testEmptyArrayIterator2Structure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder().build())) {
+            testEmptyArrayIterator2(parser);
+        }
+    }
+
+    static void testEmptyArrayIterator2(JsonParser parser) {
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+        }
+    }
+
+    public void testEmptyArrayIterator3Reader() {
+        try (JsonParser parser = Json.createParser(new StringReader("[]"))) {
+            testEmptyArrayIterator3(parser);
+        }
+    }
+
+    public void testEmptyArrayIterator3Structure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder().build())) {
+            testEmptyArrayIterator3(parser);
+        }
+    }
+
+    static void testEmptyArrayIterator3(JsonParser parser) {
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        assertEquals(false, parser.hasNext());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+        }
+    }
+
+
+    // Tests empty object
+    public void testEmptyObjectReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{}"))) {
+            testEmptyObject(parser);
+        }
+    }
+
+    public void testEmptyObjectStream() {
+        try (JsonParser parser = Json.createParser(
+                new ByteArrayInputStream(new byte[]{'{', '}'}))) {
+            testEmptyObject(parser);
+        }
+    }
+
+    public void testEmptyObjectStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder().build())) {
+            testEmptyObject(parser);
+        }
+    }
+
+    public void testEmptyObjectStructureWithConfig() {
+        Map<String, ?> config = new HashMap<>();
+        try (JsonParser parser = Json.createParserFactory(config).createParser(
+                Json.createObjectBuilder().build())) {
+            testEmptyObject(parser);
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static void testEmptyObject(JsonParser parser) {
+        while (parser.hasNext()) {
+            parser.next();
+        }
+    }
+
+
+    public void testEmptyObjectIteratorReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{}"))) {
+            testEmptyObjectIterator(parser);
+        }
+    }
+
+    public void testEmptyObjectIteratorStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder().build())) {
+            testEmptyObjectIterator(parser);
+        }
+    }
+
+    static void testEmptyObjectIterator(JsonParser parser) {
+        assertEquals(true, parser.hasNext());
+        assertEquals(true, parser.hasNext());
+        assertEquals(Event.START_OBJECT, parser.next());
+
+        assertEquals(true, parser.hasNext());
+        assertEquals(true, parser.hasNext());
+        assertEquals(Event.END_OBJECT, parser.next());
+
+        assertEquals(false, parser.hasNext());
+        assertEquals(false, parser.hasNext());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+        }
+    }
+
+
+    public void testEmptyObjectIterator2Reader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{}"))) {
+            testEmptyObjectIterator2(parser);
+        }
+    }
+
+    public void testEmptyObjectIterator2Structure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder().build())) {
+            testEmptyObjectIterator2(parser);
+        }
+    }
+
+    static void testEmptyObjectIterator2(JsonParser parser) {
+        assertEquals(Event.START_OBJECT, parser.next());
+        assertEquals(Event.END_OBJECT, parser.next());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+        }
+    }
+
+
+    public void testEmptyObjectIterator3Reader() {
+        try (JsonParser parser = Json.createParser(new StringReader("{}"))) {
+            testEmptyObjectIterator3(parser);
+        }
+    }
+
+    public void testEmptyObjectIterator3Structure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createObjectBuilder().build())) {
+            testEmptyObjectIterator3(parser);
+        }
+    }
+
+    static void testEmptyObjectIterator3(JsonParser parser) {
+        assertEquals(Event.START_OBJECT, parser.next());
+        assertEquals(Event.END_OBJECT, parser.next());
+        assertEquals(false, parser.hasNext());
+        try {
+            parser.next();
+            fail("Should have thrown a NoSuchElementException");
+        } catch (NoSuchElementException ne) {
+            // expected
+        }
+    }
+
+
+    public void testWikiIteratorReader() throws Exception {
+        try (JsonParser parser = Json.createParser(wikiReader())) {
+            testWikiIterator(parser);
+        }
+    }
+
+    public void testWikiIteratorStructure() throws Exception {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                JsonBuilderTest.buildPerson())) {
+            testWikiIterator(parser);
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static void testWikiIterator(JsonParser parser) throws Exception {
+        while (parser.hasNext()) {
+            parser.next();
+        }
+    }
+
+    public void testWikiInputStream() throws Exception {
+        try (JsonParser parser = Json.createParser(wikiStream())) {
+            testWiki(parser);
+        }
+    }
+
+    public void testWikiInputStreamUTF16LE() throws Exception {
+        ByteArrayInputStream bin = new ByteArrayInputStream(wikiString()
+                .getBytes(StandardCharsets.UTF_16LE));
+        try (JsonParser parser = Json.createParser(bin)) {
+            testWiki(parser);
+        }
+    }
+
+    public void testWikiReader() throws Exception {
+        try (JsonParser parser = Json.createParser(wikiReader())) {
+            testWiki(parser);
+        }
+    }
+
+    public void testWikiStructure() throws Exception {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                JsonBuilderTest.buildPerson())) {
+            testWiki(parser);
+        }
+    }
+
+    static void testWiki(JsonParser parser) {
+
+        Event event = parser.next();
+        assertEquals(Event.START_OBJECT, event);
+
+        testObjectStringValue(parser, "firstName", "John");
+        testObjectStringValue(parser, "lastName", "Smith");
+
+        event = parser.next();
+        assertEquals(Event.KEY_NAME, event);
+        assertEquals("age", parser.getString());
+
+        event = parser.next();
+        assertEquals(Event.VALUE_NUMBER, event);
+        assertEquals(25, parser.getInt());
+        assertEquals(25, parser.getLong());
+        assertEquals(25, parser.getBigDecimal().intValue());
+        assertTrue( parser.isIntegralNumber());
+
+        event = parser.next();
+        assertEquals(Event.KEY_NAME, event);
+        assertEquals("address", parser.getString());
+
+        event = parser.next();
+        assertEquals(Event.START_OBJECT, event);
+
+
+        testObjectStringValue(parser, "streetAddress", "21 2nd Street");
+        testObjectStringValue(parser, "city", "New York");
+        testObjectStringValue(parser, "state", "NY");
+        testObjectStringValue(parser, "postalCode", "10021");
+
+        event = parser.next();
+        assertEquals(Event.END_OBJECT, event);
+
+        event = parser.next();
+        assertEquals(Event.KEY_NAME, event);
+        assertEquals("phoneNumber", parser.getString());
+
+        event = parser.next();
+        assertEquals(Event.START_ARRAY, event);
+        event = parser.next();
+        assertEquals(Event.START_OBJECT, event);
+        testObjectStringValue(parser, "type", "home");
+        testObjectStringValue(parser, "number", "212 555-1234");
+        event = parser.next();
+        assertEquals(Event.END_OBJECT, event);
+
+        event = parser.next();
+        assertEquals(Event.START_OBJECT, event);
+        testObjectStringValue(parser, "type", "fax");
+        testObjectStringValue(parser, "number", "646 555-4567");
+        event = parser.next();
+        assertEquals(Event.END_OBJECT, event);
+        event = parser.next();
+        assertEquals(Event.END_ARRAY, event);
+
+        event = parser.next();
+        assertEquals(Event.END_OBJECT, event);
+    }
+
+    static void testObjectStringValue(JsonParser parser, String name, String value) {
+        Event event = parser.next();
+        assertEquals(Event.KEY_NAME, event);
+        assertEquals(name, parser.getString());
+
+        event = parser.next();
+        assertEquals(Event.VALUE_STRING, event);
+        assertEquals(value, parser.getString());
+    }
+
+    public void testNestedArrayReader() {
+        try (JsonParser parser = Json.createParser(new StringReader("[[],[[]]]"))) {
+            testNestedArray(parser);
+        }
+    }
+
+    public void testNestedArrayStructure() {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                Json.createArrayBuilder()
+                        .add(Json.createArrayBuilder())
+                        .add(Json.createArrayBuilder()
+                                .add(Json.createArrayBuilder()))
+                        .build())) {
+            testNestedArray(parser);
+        }
+    }
+
+    static void testNestedArray(JsonParser parser) {
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.START_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        assertEquals(Event.END_ARRAY, parser.next());
+        assertEquals(false, parser.hasNext());
+        assertEquals(false, parser.hasNext());
+    }
+
+    public void testExceptionsReader() throws Exception {
+        try (JsonParser parser = Json.createParser(wikiReader())) {
+            testExceptions(parser);
+        }
+    }
+
+    public void testExceptionsStructure() throws Exception {
+        try (JsonParser parser = Json.createParserFactory(null).createParser(
+                JsonBuilderTest.buildPerson())) {
+            testExceptions(parser);
+        }
+    }
+
+    static void testExceptions(JsonParser parser) {
+
+        Event event = parser.next();
+        assertEquals(Event.START_OBJECT, event);
+
+        try {
+            parser.getString();
+            fail("JsonParser#getString() should have thrown exception in START_OBJECT state");
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+
+        try {
+            parser.isIntegralNumber();
+            fail("JsonParser#getNumberType() should have thrown exception in START_OBJECT state");
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+
+        try {
+            parser.getInt();
+            fail("JsonParser#getInt() should have thrown exception in START_OBJECT state");
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+
+        try {
+            parser.getLong();
+            fail("JsonParser#getLong() should have thrown exception in START_OBJECT state");
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+
+        try {
+            parser.getBigDecimal();
+            fail("JsonParser#getBigDecimal() should have thrown exception in START_OBJECT state");
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+    }
+
+    static String wikiString() {
+        String str;
+        try (Scanner scanner = new Scanner(wikiReader())
+                .useDelimiter("\\A")) {
+            str = scanner.hasNext() ? scanner.next() : "";
+        }
+        return str;
+    }
+
+    static InputStream wikiStream() {
+        return JsonParserTest.class.getResourceAsStream("/wiki.json");
+    }
+
+    static Reader wikiReader() {
+        return new InputStreamReader(
+                JsonParserTest.class.getResourceAsStream("/wiki.json"), StandardCharsets.UTF_8);
+    }
+
+    public void testIntNumber() {
+        JsonParserFactory factory = Json.createParserFactory(null);
+
+        Random r = new Random(System.currentTimeMillis());
+
+        for(int i=0; i < 100000; i++) {
+            long num = i%2 == 0 ? r.nextInt() : r.nextLong();
+            try (JsonParser parser = factory.createParser(new StringReader("["+num+"]"))) {
+                parser.next();
+                parser.next();
+                assertEquals("Fails for num="+num, new BigDecimal(num).intValue(), parser.getInt());
+            }
+        }
+
+    }
+
+    public void testBigDecimalGetString() {
+        JsonParserFactory f = Json.createParserFactory(null);
+        JsonObject obj = Json.createObjectBuilder().add("a", BigDecimal.ONE).build();
+        try (JsonParser parser = f.createParser(obj)) {
+            parser.next();
+            parser.next();
+            parser.next();
+            assertEquals("Fails for BigDecimal=1", "1", parser.getString());
+        }
+    }
+
+    public void testIntGetString() {
+        JsonParserFactory f = Json.createParserFactory(null);
+        JsonObject obj = Json.createObjectBuilder().add("a", 5).build();
+        try (JsonParser parser = f.createParser(obj)) {
+            parser.next();
+            parser.next();
+            parser.next();
+            assertEquals("Fails for int=5", "5", parser.getString());
+        }
+    }
+    static class MyBufferPool implements BufferPool {
+        private boolean takeCalled;
+        private boolean recycleCalled;
+        private final char[] buf;
+
+        MyBufferPool(int size) {
+            buf = new char[size];
+        }
+
+        @Override
+        public char[] take() {
+            takeCalled = true;
+            return buf;
+        }
+
+        @Override
+        public void recycle(char[] buf) {
+            recycleCalled = true;
+        }
+
+        boolean isTakeCalled() {
+            return takeCalled;
+        }
+
+        boolean isRecycleCalled() {
+            return recycleCalled;
+        }
+    }
+
+    public void testBufferPoolFeature() {
+        final MyBufferPool bufferPool = new MyBufferPool(1024);
+        Map<String, Object> config = new HashMap<String, Object>() {{
+            put(BufferPool.class.getName(), bufferPool);
+        }};
+
+        JsonParserFactory factory = Json.createParserFactory(config);
+        try (JsonParser parser = factory.createParser(new StringReader("[]"))) {
+            parser.next();
+            parser.next();
+        }
+        assertTrue(bufferPool.isTakeCalled());
+        assertTrue(bufferPool.isRecycleCalled());
+    }
+
+    public void testBufferSizes() {
+        Random r = new Random(System.currentTimeMillis());
+        for(int size=100; size < 1000; size++) {
+            final MyBufferPool bufferPool = new MyBufferPool(size);
+            Map<String, Object> config = new HashMap<String, Object>() {{
+                put(BufferPool.class.getName(), bufferPool);
+            }};
+            JsonParserFactory factory = Json.createParserFactory(config);
+
+            StringBuilder sb = new StringBuilder();
+            for(int i=0; i < 1000; i++) {
+                sb.append('a');
+                String name = sb.toString();
+                long num = i%2 == 0 ? r.nextInt() : r.nextLong();
+                String str = "{\""+name+"\":["+num+"]}";
+                try (JsonParser parser = factory.createParser(new StringReader(str))) {
+                    parser.next();
+                    parser.next();
+                    assertEquals("Fails for " + str, name, parser.getString());
+                    parser.next();
+                    parser.next();
+                    assertEquals("Fails for "+str, new BigDecimal(num).intValue(), parser.getInt());
+                }
+            }
+        }
+    }
+
+    // Tests for string starting on buffer boundary (JSONP-15)
+    // xxxxxxx"xxxxxxxxx"
+    //        ^
+    //        |
+    //       4096
+    public void testStringUsingStandardBuffer() throws Throwable {
+        JsonParserFactory factory = Json.createParserFactory(null);
+        StringBuilder sb = new StringBuilder();
+        for(int i=0; i < 40000; i++) {
+            sb.append('a');
+            String name = sb.toString();
+            String str = "{\""+name+"\":\""+name+"\"}";
+            try (JsonParser parser = factory.createParser(new StringReader(str))) {
+                parser.next();
+                parser.next();
+                assertEquals("Fails for size=" + i, name, parser.getString());
+                parser.next();
+                assertEquals("Fails for size=" + i, name, parser.getString());
+            } catch (Throwable e) {
+                throw new Throwable("Failed for size=" + i, e);
+            }
+        }
+    }
+
+    // Tests for int starting on buffer boundary
+    // xxxxxxx"xxxxxxxxx"
+    //        ^
+    //        |
+    //       4096
+    public void testIntegerUsingStandardBuffer() throws Throwable {
+        Random r = new Random(System.currentTimeMillis());
+        JsonParserFactory factory = Json.createParserFactory(null);
+        StringBuilder sb = new StringBuilder();
+        for(int i=0; i < 40000; i++) {
+            sb.append('a');
+            String name = sb.toString();
+            int num = r.nextInt();
+            String str = "{\"" + name + "\":" + num + "}";
+            try (JsonParser parser = factory.createParser(new StringReader(str))) {
+                parser.next();
+                parser.next();
+                assertEquals("Fails for size=" + i, name, parser.getString());
+                parser.next();
+                assertEquals("Fails for size=" + i, num, parser.getInt());
+            } catch (Throwable e) {
+                throw new Throwable("Failed for size=" + i, e);
+            }
+        }
+    }
+
+    public void testStringUsingBuffers() throws Throwable {
+        for(int size=20; size < 500; size++) {
+            final MyBufferPool bufferPool = new MyBufferPool(size);
+            Map<String, Object> config = new HashMap<String, Object>() {{
+                put(BufferPool.class.getName(), bufferPool);
+            }};
+            JsonParserFactory factory = Json.createParserFactory(config);
+
+            StringBuilder sb = new StringBuilder();
+            for(int i=0; i < 1000; i++) {
+                sb.append('a');
+                String name = sb.toString();
+                String str = "{\""+name+"\":\""+name+"\"}";
+                JsonLocation location;
+                try (JsonParser parser = factory.createParser(new StringReader(str))) {
+                    parser.next();
+                    parser.next();
+                    assertEquals("name fails for buffer size=" + size + " name length=" + i, name, parser.getString());
+                    location = parser.getLocation();
+                    assertEquals("Stream offset fails for buffer size=" + size + " name length=" + i,
+                            name.length() + 3, location.getStreamOffset());
+                    assertEquals("Column value fails for buffer size=" + size + " name length=" + i,
+                            name.length() + 4, location.getColumnNumber());
+                    assertEquals("Line value fails for buffer size=" + size + " name length=" + i,
+                            1, location.getLineNumber());
+
+                    parser.next();
+                    assertEquals("value fails for buffer size=" + size + " name length=" + i, name, parser.getString());
+                    location = parser.getLocation();
+                    assertEquals("Stream offset fails for buffer size=" + size + " name length=" + i, 2 * name.length() + 6, location.getStreamOffset());
+                    assertEquals("Column value fails for buffer size=" + size + " name length=" + i,
+                            2 * name.length() + 7, location.getColumnNumber());
+                    assertEquals("Line value fails for buffer size=" + size + " name length=" + i,
+                            1, location.getLineNumber());
+                } catch (Throwable e) {
+                    throw new Throwable("Failed for buffer size=" + size + " name length=" + i, e);
+                }
+            }
+        }
+    }
+
+    public void testExceptionsFromHasNext() {
+        checkExceptionFromHasNext("{");
+        checkExceptionFromHasNext("{\"key\"");
+        checkExceptionFromHasNext("{\"name\" : \"prop\"");
+        checkExceptionFromHasNext("{\"name\" : 3");
+        checkExceptionFromHasNext("{\"name\" : true");
+        checkExceptionFromHasNext("{\"name\" : null");
+        checkExceptionFromHasNext("{\"name\" : {\"$eq\":\"cdc\"}");
+        checkExceptionFromHasNext("{\"name\" : [{\"$eq\":\"cdc\"}]");
+        checkExceptionFromHasNext("[");
+        checkExceptionFromHasNext("{\"name\" : [{\"key\" : [[{\"a\" : 1}]");
+        checkExceptionFromHasNext("{\"unique\":true,\"name\":\"jUnitTestIndexNeg005\", \"fields\":[{\"order\":-1,\"path\":\"city.zip\"}");
+    }
+
+    public void testEOFFromHasNext() {
+        checkExceptionFromHasNext("{ \"d\" : 1 } 2 3 4");
+        checkExceptionFromHasNext("[ {\"d\" : 1 }] 2 3 4");
+        checkExceptionFromHasNext("1 2 3 4");
+        checkExceptionFromHasNext("null 2 3 4");
+    }
+
+    public void testExceptionsFromNext() {
+        checkExceptionFromNext("{\"name\" : fal");
+        checkExceptionFromNext("{\"name\" : nu");
+        checkExceptionFromNext("{\"name\" : \"pro");
+        checkExceptionFromNext("{\"key\":");
+        checkExceptionFromNext("fal");
+    }
+
+    private void checkExceptionFromHasNext(String input) {
+        try (JsonParser parser = Json.createParser(new StringReader(input))) {
+            try {
+                while (parser.hasNext()) {
+                    try {
+                        parser.next();
+                    } catch (Throwable t1) {
+                        fail("Exception should occur from hasNext() for '" + input + "'");
+                    }
+                }
+            } catch (JsonParsingException t) {
+                //this is OK
+                return;
+            }
+        }
+        fail();
+    }
+
+    private void checkExceptionFromNext(String input) {
+        try (JsonParser parser = Json.createParser(new StringReader(input))) {
+            while (parser.hasNext()) {
+                try {
+                    parser.next();
+                } catch (JsonParsingException t) {
+                    //this is OK
+                    return;
+                }
+            }
+        }
+        fail();
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java
new file mode 100644
index 0000000..425ca52
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParsingException;
+import java.io.StringReader;
+
+/**
+ * JsonParsingException Tests
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonParsingExceptionTest extends TestCase {
+
+    public void testWrongJson() {
+        // testMalformedJson("", null); Allowed in 1.1
+    }
+
+    public void testWrongJson1() {
+        // testMalformedJson("{}{}", null);  Allowed in 1.1
+    }
+
+    public void testWrongJson2() {
+        // testMalformedJson("{", null);  Allowed in 1.1
+    }
+
+    public void testWrongJson3() {
+        testMalformedJson("{[]", null);
+    }
+
+    public void testWrongJson4() {
+        testMalformedJson("{]", null);
+    }
+
+    public void testWrongJson5() {
+        testMalformedJson("{\"a\":[]]", null);
+    }
+
+    public void testWrongJson6() {
+        testMalformedJson("[ {}, [] }", null);
+    }
+
+    public void testWrongJson61() {
+        testMalformedJson("[ {}, {} }", null);
+    }
+
+    public void testWrongJson7() {
+        testMalformedJson("{ \"a\" : {}, \"b\": {} ]", null);
+    }
+
+    public void testWrongJson8() {
+        testMalformedJson("{ \"a\" : {}, \"b\": [] ]", null);
+    }
+
+    public void testWrongUnicode() {
+        testMalformedJson("[ \"\\uX00F\" ]", null);
+        testMalformedJson("[ \"\\u000Z\" ]", null);
+        testMalformedJson("[ \"\\u000\" ]", null);
+        testMalformedJson("[ \"\\u00\" ]", null);
+        testMalformedJson("[ \"\\u0\" ]", null);
+        testMalformedJson("[ \"\\u\" ]", null);
+        testMalformedJson("[ \"\\u\"", null);
+        testMalformedJson("[ \"\\", null);
+    }
+
+    public void testControlChar() {
+        testMalformedJson("[ \"\u0000\" ]", null);
+        testMalformedJson("[ \"\u000c\" ]", null);
+        testMalformedJson("[ \"\u000f\" ]", null);
+        testMalformedJson("[ \"\u001F\" ]", null);
+        testMalformedJson("[ \"\u001f\" ]", null);
+    }
+
+    public void testLocation1() {
+        testMalformedJson("x", new MyLocation(1, 1, 0));
+        testMalformedJson("{]", new MyLocation(1, 2, 1));
+        testMalformedJson("[}", new MyLocation(1, 2, 1));
+        testMalformedJson("[a", new MyLocation(1, 2, 1));
+        testMalformedJson("[nuLl]", new MyLocation(1, 4, 3));
+        testMalformedJson("[falsE]", new MyLocation(1, 6, 5));
+        // testMalformedJson("[][]", new MyLocation(1, 3, 2));   allowed in 1.1
+        testMalformedJson("[1234L]", new MyLocation(1, 6, 5));
+    }
+
+    public void testLocation2() {
+        testMalformedJson("[null\n}", new MyLocation(2, 1, 6));
+        testMalformedJson("[null\r\n}", new MyLocation(2, 1, 7));
+        testMalformedJson("[null\n, null\n}", new MyLocation(3, 1, 13));
+        testMalformedJson("[null\r\n, null\r\n}", new MyLocation(3, 1, 15));
+    }
+
+    private void testMalformedJson(String json, JsonLocation expected) {
+        try (JsonParser parser = Json.createParser(new StringReader(json))) {
+            while (parser.hasNext()) {
+                parser.next();
+            }
+            fail("Expected to throw JsonParsingException for " + json);
+        } catch (JsonParsingException je) {
+            // Expected
+            if (expected != null) {
+                JsonLocation got = je.getLocation();
+                assertEquals(expected.getLineNumber(), got.getLineNumber());
+                assertEquals(expected.getColumnNumber(), got.getColumnNumber());
+                assertEquals(expected.getStreamOffset(), got.getStreamOffset());
+            }
+        }
+    }
+
+    private static class MyLocation implements JsonLocation {
+        private final long columnNo;
+        private final long lineNo;
+        private final long streamOffset;
+
+        MyLocation(long lineNo, long columnNo, long streamOffset) {
+            this.lineNo = lineNo;
+            this.columnNo = columnNo;
+            this.streamOffset = streamOffset;
+        }
+
+        @Override
+        public long getLineNumber() {
+            return lineNo;
+        }
+
+        @Override
+        public long getColumnNumber() {
+            return columnNo;
+        }
+
+        @Override
+        public long getStreamOffset() {
+            return streamOffset;
+        }
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java
new file mode 100644
index 0000000..5ba3b92
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonStructure;
+
+import java.io.StringReader;
+
+import org.junit.Test;
+
+/**
+ *
+ * @author lukas
+ */
+public class JsonPatchBugsTest {
+
+    // https://github.com/javaee/jsonp/issues/58
+    @Test(expected = JsonException.class)
+    public void applyThrowsJsonException() {
+        JsonArray array = Json.createArrayBuilder()
+                .add(Json.createObjectBuilder()
+                        .add("name", "Bob")
+                        .build())
+                .build();
+        JsonPatch patch = Json.createPatchBuilder()
+                .replace("/0/name", "Bobek")
+                .replace("/1/name", "Vila Amalka")
+                .build();
+        JsonArray result = patch.apply(array);
+    }
+
+    // https://github.com/eclipse-ee4j/jsonp/issues/181
+    @Test(expected = JsonException.class)
+    public void applyThrowsJsonException2() {
+        // JSON document to be patched
+        String targetDocument
+                = "{\n"
+                + "  \"firstName\": \"John\",\n"
+                + "  \"lastName\": \"Doe\"\n"
+                + "}";
+
+        // JSON Patch document
+        // Instead of "op", we have "op_", which is invalid
+        String patchDocument
+                = "[\n"
+                + "  { \"op_\": \"replace\", \"path\": \"/firstName\", \"value\": \"Jane\" }\n"
+                + "]";
+
+        try (JsonReader objectReader = Json.createReader(new StringReader(targetDocument));
+                JsonReader arrayReader = Json.createReader(new StringReader(patchDocument))) {
+
+            JsonStructure target = objectReader.read();
+            JsonPatch patch = Json.createPatch(arrayReader.readArray());
+
+            // Applies the patch
+            // It will throw a NullPointerException with no message
+            JsonStructure patched = patch.apply(target);
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java
new file mode 100644
index 0000000..ded202f
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+
+import org.junit.Test;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+public class JsonPatchBuilderTest {
+
+    @Test
+    public void shouldBuildJsonPatchExpressionUsingJsonPatchBuilder() {
+        JsonPatchBuilder patchBuilder = Json.createPatchBuilder();
+        JsonObject result = patchBuilder.add("/email", "john@example.com")
+                    .replace("/age", 30)
+                    .remove("/phoneNumber")
+                    .test("/firstName", "John")
+                    .copy("/address/lastName", "/lastName")
+                    .build()
+                    .apply(buildPerson());
+        assertThat(result, is(expectedBuildPerson()));
+        
+    }
+
+    static JsonObject expectedBuildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("email", "john@example.com")
+                .add("age", 30)
+                .add("address", Json.createObjectBuilder()
+                        .add("lastName", "Smith")
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .build();
+    }
+
+    static JsonObject buildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java
new file mode 100644
index 0000000..4743b68
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPatchDiffTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        List<Object[]> examples = new ArrayList<>();
+        JsonArray data = JsonPatchDiffTest.loadData();
+        for (JsonValue jsonValue : data) {
+            JsonObject test = (JsonObject) jsonValue;
+            Object[] testData = new Object[4];
+            testData[0] = test.get("original");
+            testData[1] = test.get("target");
+            testData[2] = test.get("expected");
+            testData[3] = createExceptionClass((JsonString)test.get("exception"));
+
+            examples.add(testData);
+        }
+
+        return examples;
+    }
+
+    private static Class<? extends Exception> createExceptionClass(
+            JsonString exceptionClassName) throws ClassNotFoundException {
+        if (exceptionClassName != null) {
+            return (Class<? extends Exception>) Class
+                    .forName(exceptionClassName.getString());
+        }
+        return null;
+    }
+
+    private static JsonArray loadData() {
+        InputStream testData = JsonPatchTest.class
+                .getResourceAsStream("/jsonpatchdiff.json");
+
+        JsonArray data;
+        try(JsonReader reader = Json.createReader(testData)){
+            data = (JsonArray) reader.read();
+        }
+
+        return data;
+    }
+
+    private JsonStructure original;
+    private JsonStructure target;
+    private JsonValue expected;
+    private Class<? extends Exception> expectedException;
+
+    public JsonPatchDiffTest(JsonStructure original, JsonStructure target,
+            JsonValue expected, Class<? extends Exception> expectedException) {
+        super();
+        this.original = original;
+        this.target = target;
+        this.expected = expected;
+        this.expectedException = expectedException;
+    }
+
+    @Test
+    public void shouldExecuteJsonPatchDiffOperationsToJsonDocument() {
+        try {
+            JsonPatch diff = Json.createDiff(this.original, this.target);
+            assertThat(diff, is(Json.createPatchBuilder((JsonArray) expected).build()));
+            assertThat(expectedException, nullValue());
+        } catch (Exception e) {
+            if (expectedException == null) {
+                fail(e.getMessage());
+            } else {
+                assertThat(e, instanceOf(expectedException));
+            }
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java
new file mode 100644
index 0000000..0742cbb
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import jakarta.json.JsonException;
+import jakarta.json.JsonPatch.Operation;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author lukas
+ */
+public class JsonPatchOperationTest {
+
+    private static final String[] opNames = {"add", "remove", "replace", "move", "copy", "test"};
+
+    @Test
+    public void fromOperationName() {
+        for (String op: opNames) {
+            Assert.assertEquals(Operation.valueOf(op.toUpperCase()), Operation.fromOperationName(op));
+        }
+        for (String op: opNames) {
+            Assert.assertEquals(Operation.valueOf(op.toUpperCase()), Operation.fromOperationName(op.toUpperCase()));
+        }
+    }
+
+    @Test(expected = JsonException.class)
+    public void fromInvalidOperationName() {
+        Operation.fromOperationName("undef");
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPatchTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPatchTest.java
new file mode 100644
index 0000000..08f3004
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPatchTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPatchTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        List<Object[]> examples = new ArrayList<>();
+        JsonArray data = loadData();
+        for (JsonValue jsonValue : data) {
+            JsonObject test = (JsonObject) jsonValue;
+            Object[] testData = new Object[4];
+            testData[0] = createPatchArray(test.get("op"));
+            testData[1] = test.get("target");
+            testData[2] = test.get("expected");
+            testData[3] = createExceptionClass((JsonString)test.get("exception"));
+
+            examples.add(testData);
+        }
+
+        return examples;
+    }
+
+    private static Class<? extends Exception> createExceptionClass(
+            JsonString exceptionClassName) throws ClassNotFoundException {
+        if (exceptionClassName != null) {
+            return (Class<? extends Exception>) Class
+                    .forName(exceptionClassName.getString());
+        }
+        return null;
+    }
+
+    private static JsonArray createPatchArray(JsonValue object) {
+        return Json.createArrayBuilder().add(object).build();
+    }
+
+    private static JsonArray loadData() {
+        InputStream testData = JsonPatchTest.class
+                .getResourceAsStream("/jsonpatch.json");
+        JsonReader reader = Json.createReader(testData);
+        return (JsonArray) reader.read();
+    }
+
+    private JsonArray patch;
+    private JsonStructure target;
+    private JsonValue expected;
+    private Class<? extends Exception> expectedException;
+
+    public JsonPatchTest(JsonArray patch, JsonStructure target,
+            JsonValue expected, Class<? extends Exception> expectedException) {
+        super();
+        this.patch = patch;
+        this.target = target;
+        this.expected = expected;
+        this.expectedException = expectedException;
+    }
+
+    @Test
+    public void shouldExecuteJsonPatchOperationsToJsonDocument() {
+        try {
+            JsonPatch patch = Json.createPatchBuilder(this.patch).build();
+            JsonStructure output = patch.apply(target);
+            assertThat(output, is(expected));
+            assertThat(expectedException, nullValue());
+        } catch (Exception e) {
+            if (expectedException == null) {
+                fail(e.getMessage());
+            } else {
+                assertThat(e, instanceOf(expectedException));
+            }
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java
new file mode 100644
index 0000000..5fe2fe6
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPointerAddOperationTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        return Arrays.asList(new Object[][] { 
+                 {buildSimpleAddPatch(), buildAddress(), buildExpectedAddress() },
+                 {buildComplexAddPatch(), buildPerson(), buildExpectedPerson()},
+                 {buildArrayAddPatchInPosition(), buildPerson(), buildExpectedPersonConcreteArrayPosition()},
+                 {buildArrayAddPatchInLastPosition(), buildPerson(), buildExpectedPersonArrayLastPosition()}
+           });
+    }
+
+    private JsonObject pathOperation;
+    private JsonStructure target;
+    private JsonValue expectedResult;
+
+    public JsonPointerAddOperationTest(JsonObject pathOperation,
+                                       JsonStructure target, JsonValue expectedResult) {
+        super();
+        this.pathOperation = pathOperation;
+        this.target = target;
+        this.expectedResult = expectedResult;
+    }
+
+    @Test
+    public void shouldAddElementsToExistingJsonDocument() {
+        JsonPointer pointer = Json.createPointer(pathOperation.getString("path"));
+        JsonObject modified = (JsonObject) pointer.add(target, pathOperation.get("value"));
+        assertThat(modified, is(expectedResult));
+    }
+
+    static JsonObject buildAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildComplexAddPatch() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/address/streetAddress")
+                .add("value", "myaddress")
+                .build();
+    }
+    static JsonObject buildSimpleAddPatch() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/streetAddress")
+                .add("value", "myaddress")
+                .build();
+    }
+    static JsonObject buildArrayAddPatchInPosition() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/phoneNumber/0")
+                .add("value", Json.createObjectBuilder()
+                        .add("type", "home")
+                        .add("number", "200 555-1234"))
+                .build();
+    }
+    static JsonObject buildArrayAddPatchInLastPosition() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/phoneNumber/-")
+                .add("value", Json.createObjectBuilder()
+                        .add("type", "home")
+                        .add("number", "200 555-1234"))
+                .build();
+    }
+    static JsonObject buildExpectedAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "myaddress")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildExpectedPersonConcreteArrayPosition() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add((Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "200 555-1234")))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildExpectedPersonArrayLastPosition() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567"))
+                         .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "200 555-1234")))
+                .build();
+    }
+    static JsonObject buildExpectedPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "myaddress")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java
new file mode 100644
index 0000000..904d405
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import org.junit.Test;
+
+import jakarta.json.Json;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JSON pointer escape/unescape tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class JsonPointerEscapeTest {
+
+    @Test
+    public void escapeTest() {
+        assertEquals("a~1b", Json.encodePointer("a/b"));
+        assertEquals("a~0b~1c", Json.encodePointer("a~b/c"));
+    }
+
+    @Test
+    public void unescapeTest() {
+        assertEquals("/a/b", Json.decodePointer("/a~1b"));
+        assertEquals("/~1", Json.decodePointer("/~01"));
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java
new file mode 100644
index 0000000..1211261
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPointerRemoveOperationTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        return Arrays.asList(new Object[][] { 
+                 {buildSimpleRemovePatch(), buildAddress(), buildExpectedRemovedAddress() },
+                 {buildComplexRemovePatch(), buildPerson(), buildExpectedPersonWithoutStreetAddress()},
+                 {buildArrayRemovePatchInPosition(), buildPerson(), buildPersonWithoutFirstPhone()}
+           });
+    }
+
+    private JsonObject pathOperation;
+    private JsonStructure target;
+    private JsonValue expectedResult;
+
+    public JsonPointerRemoveOperationTest(JsonObject pathOperation,
+                                          JsonObject target, JsonValue expectedResult) {
+        super();
+        this.pathOperation = pathOperation;
+        this.target = target;
+        this.expectedResult = expectedResult;
+    }
+
+    @Test
+    public void shouldRemoveElementsToExistingJsonDocument() {
+        JsonPointer pointer = Json.createPointer(pathOperation.getString("path"));
+        JsonObject modified = (JsonObject) pointer.remove(target);
+        assertThat(modified, is(expectedResult));
+    }
+
+    static JsonObject buildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildComplexRemovePatch() {
+        return Json.createObjectBuilder()
+                .add("op", "remove")
+                .add("path", "/address/streetAddress")
+                .build();
+    }
+    static JsonObject buildSimpleRemovePatch() {
+        return Json.createObjectBuilder()
+                .add("op", "remove")
+                .add("path", "/streetAddress")
+                .build();
+    }
+    static JsonObject buildArrayRemovePatchInPosition() {
+        return Json.createObjectBuilder()
+                .add("op", "remove")
+                .add("path", "/phoneNumber/0")
+                .build();
+    }
+    static JsonObject buildExpectedRemovedAddress() {
+        return Json.createObjectBuilder()
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildPersonWithoutFirstPhone() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildExpectedPersonWithoutStreetAddress() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java
new file mode 100644
index 0000000..a4661d9
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+
+import jakarta.json.Json;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPointerReplaceOperationTest {
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        return Arrays.asList(new Object[][] { 
+                 {buildSimpleReplacePatch(), buildAddress(), buildExpectedAddress(), null},
+                 {buildComplexReplacePatch(), buildPerson(), buildExpectedPerson(), null},
+                 {buildArrayReplacePatchInPosition(), buildPerson(), buildExpectedPersonConcreteArrayPosition(), null},
+                 {buildArrayAddPatchInLastPosition(), buildPerson(), null, JsonException.class},
+                 {buildNoneExistingReplacePatch(), buildAddress(), null, JsonException.class}
+           });
+    }
+
+    private JsonObject pathOperation;
+    private JsonStructure target;
+    private JsonValue expectedResult;
+    private Class<? extends Exception> expectedException;
+
+    public JsonPointerReplaceOperationTest(JsonObject pathOperation,
+            JsonStructure target, JsonValue expectedResult, Class<? extends Exception> expectedException) {
+        super();
+        this.pathOperation = pathOperation;
+        this.target = target;
+        this.expectedResult = expectedResult;
+        this.expectedException = expectedException;
+    }
+
+    @Test
+    public void shouldReplaceElementsToExistingJsonDocument() {
+        try {
+        JsonPointer pointer = Json.createPointer(pathOperation.getString("path"));
+        JsonObject modified = (JsonObject) pointer.replace(target, pathOperation.get("value"));
+        assertThat(modified, is(expectedResult));
+        assertThat(expectedException, nullValue());
+        } catch(Exception e) {
+            if(expectedException == null) {
+                fail(e.getMessage());
+            } else {
+                assertThat(e, instanceOf(expectedException));
+            }
+        }
+    }
+
+    static JsonObject buildAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "21 2nd Street")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildComplexReplacePatch() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/address/streetAddress")
+                .add("value", "myaddress")
+                .build();
+    }
+    static JsonObject buildSimpleReplacePatch() {
+        return Json.createObjectBuilder()
+                .add("op", "replace")
+                .add("path", "/streetAddress")
+                .add("value", "myaddress")
+                .build();
+    }
+    static JsonObject buildNoneExistingReplacePatch() {
+        return Json.createObjectBuilder()
+                .add("op", "replace")
+                .add("path", "/notexists")
+                .add("value", "myaddress")
+                .build();
+    }
+    static JsonObject buildArrayReplacePatchInPosition() {
+        return Json.createObjectBuilder()
+                .add("op", "replace")
+                .add("path", "/phoneNumber/0")
+                .add("value", Json.createObjectBuilder()
+                        .add("type", "home")
+                        .add("number", "200 555-1234"))
+                .build();
+    }
+    static JsonObject buildArrayAddPatchInLastPosition() {
+        return Json.createObjectBuilder()
+                .add("op", "add")
+                .add("path", "/phoneNumber/-")
+                .add("value", Json.createObjectBuilder()
+                        .add("type", "home")
+                        .add("number", "200 555-1234"))
+                .build();
+    }
+    static JsonObject buildExpectedAddress() {
+        return Json.createObjectBuilder()
+                .add("streetAddress", "myaddress")
+                .add("city", "New York")
+                .add("state", "NY")
+                .add("postalCode", "10021")
+                .build();
+    }
+    static JsonObject buildPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildExpectedPersonConcreteArrayPosition() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "21 2nd Street")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add((Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "200 555-1234")))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    static JsonObject buildExpectedPerson() {
+        return Json.createObjectBuilder()
+                .add("firstName", "John")
+                .add("lastName", "Smith")
+                .add("age", 25)
+                .add("address", Json.createObjectBuilder()
+                        .add("streetAddress", "myaddress")
+                        .add("city", "New York")
+                        .add("state", "NY")
+                        .add("postalCode", "10021"))
+                .add("phoneNumber", Json.createArrayBuilder()
+                        .add(Json.createObjectBuilder()
+                                .add("type", "home")
+                                .add("number", "212 555-1234"))
+                        .add(Json.createObjectBuilder()
+                                .add("type", "fax")
+                                .add("number", "646 555-4567")))
+                .build();
+    }
+    
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerTest.java
new file mode 100644
index 0000000..6615e2f
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.instanceOf;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Arrays;
+
+import jakarta.json.Json;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * 
+ * @author Alex Soto
+ *
+ */
+@RunWith(Parameterized.class)
+public class JsonPointerTest {
+
+    private static JsonObject rfc6901Example;
+
+    @Parameters(name = "{index}: ({0})={1}")
+    public static Iterable<Object[]> data() throws Exception {
+        rfc6901Example = JsonPointerTest.readRfc6901Example();
+        return Arrays.asList(new Object[][] { 
+                 {Json.createPointer(""), rfc6901Example, null },
+                 {Json.createPointer("/foo"), rfc6901Example.getJsonArray("foo"), null},
+                 {Json.createPointer("/foo/0"), rfc6901Example.getJsonArray("foo").get(0), null},
+                 {Json.createPointer("/foo/5"), null, JsonException.class},
+                 {Json.createPointer("/p/1"), null, JsonException.class},
+                 {Json.createPointer("/"), rfc6901Example.getJsonNumber(""), null},
+                 {Json.createPointer("/a~1b"), rfc6901Example.getJsonNumber("a/b"), null},
+                 {Json.createPointer("/m~0n"), rfc6901Example.getJsonNumber("m~n"), null},
+                 {Json.createPointer("/c%d"), rfc6901Example.getJsonNumber("c%d"), null},
+                 {Json.createPointer("/e^f"), rfc6901Example.getJsonNumber("e^f"), null},
+                 {Json.createPointer("/g|h"), rfc6901Example.getJsonNumber("g|h"), null},
+                 {Json.createPointer("/i\\j"), rfc6901Example.getJsonNumber("i\\j"), null},
+                 {Json.createPointer("/k\"l"), rfc6901Example.getJsonNumber("k\"l"), null},
+                 {Json.createPointer("/ "), rfc6901Example.getJsonNumber(" "), null},
+                 {Json.createPointer("/notexists"), null, JsonException.class},
+                 {Json.createPointer("/s/t"), null, JsonException.class},
+                 {Json.createPointer("/o"), JsonObject.NULL, null}
+           });
+    }
+
+    private JsonPointer pointer;
+    private JsonValue expected;
+    private Class<? extends Exception> expectedException;
+
+    public JsonPointerTest(JsonPointer pointer, JsonValue expected, Class<? extends Exception> expectedException) {
+        super();
+        this.pointer = pointer;
+        this.expected = expected;
+        this.expectedException = expectedException;
+    }
+
+    @Test
+    public void shouldEvaluateJsonPointerExpressions() {
+        try {
+            JsonValue result = pointer.getValue(rfc6901Example);
+            assertThat(result, is(expected));
+            assertThat(expectedException, nullValue());
+        } catch(Exception e) {
+            if(expectedException == null) {
+                fail(e.getMessage());
+            } else {
+                assertThat(e, instanceOf(expectedException));
+            }
+        }
+    }
+
+    static JsonObject readRfc6901Example() throws Exception {
+        Reader rfc6901Reader = new InputStreamReader(JsonReaderTest.class.getResourceAsStream("/rfc6901.json"));
+        JsonReader reader = Json.createReader(rfc6901Reader);
+        JsonValue value = reader.readObject();
+        reader.close();
+        return (JsonObject) value;
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonPointerToStringTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonPointerToStringTest.java
new file mode 100644
index 0000000..4da85b6
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonPointerToStringTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+
+import java.util.Arrays;
+
+import jakarta.json.Json;
+import jakarta.json.JsonPointer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * JSON pointer toString tests.
+ *
+ * @author leadpony
+ */
+@RunWith(Parameterized.class)
+public class JsonPointerToStringTest {
+
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object> data() {
+        return Arrays.asList("", "/", "/one/two/3", "/a~1b", "/m~0n");
+    }
+
+    private final String expected;
+
+    public JsonPointerToStringTest(String expected) {
+        this.expected = expected;
+    }
+
+    @Test
+    public void shouldReturnOriginalEscapedString() {
+        JsonPointer pointer = Json.createPointer(expected);
+        assertThat(pointer.toString(), is(equalTo(expected)));
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonReaderTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonReaderTest.java
new file mode 100644
index 0000000..a7fca4d
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonReaderTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.JsonValue;
+
+import org.glassfish.json.api.BufferPool;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonReaderTest extends TestCase {
+    public JsonReaderTest(String testName) {
+        super(testName);
+    }
+
+    public void testObject() throws Exception {
+        JsonObject person = readPerson();
+        JsonObjectTest.testPerson(person);
+    }
+
+    public void testEscapedString() throws Exception {
+        // u00ff is escaped once, not escaped once
+        JsonReader reader = Json.createReader(new StringReader("[\"\\u0000\\u00ff\u00ff\"]"));
+        JsonArray array = reader.readArray();
+        reader.close();
+        String str = array.getString(0);
+        assertEquals("\u0000\u00ff\u00ff", str);
+    }
+
+    public void testPrimitiveIntNumbers() {
+        String[] borderlineCases = new String[]{
+                "214748364",
+                Integer.toString(Integer.MAX_VALUE),
+                Long.toString(Integer.MAX_VALUE + 1L),
+                "-214748364",
+                Integer.toString(Integer.MIN_VALUE),
+                Long.toString(Integer.MIN_VALUE - 1L)
+        };
+        for (String num : borderlineCases) {
+            JsonReader reader = Json.createReader(new StringReader("["+num+"]"));
+            try {
+                JsonArray array = reader.readArray();
+                JsonNumber value = (JsonNumber) array.get(0);
+                assertEquals("Fails for num="+num, new BigInteger(num).longValue(), value.longValue());
+            } finally {
+                reader.close();
+            }
+        }
+    }
+    
+    public void testPrimitiveLongNumbers() {
+        String[] borderlineCases = new String[]{
+                "922337203685477580",
+                Long.toString(Long.MAX_VALUE),
+                new BigInteger(Long.toString(Long.MAX_VALUE)).add(BigInteger.ONE).toString(),
+                "-922337203685477580",
+                Long.toString(Long.MIN_VALUE),
+                new BigInteger(Long.toString(Long.MIN_VALUE)).subtract(BigInteger.ONE).toString()
+        };
+        for (String num : borderlineCases) {
+            JsonReader reader = Json.createReader(new StringReader("["+num+"]"));
+            try {
+                JsonArray array = reader.readArray();
+                JsonNumber value = (JsonNumber) array.get(0);
+                assertEquals("Fails for num="+num, new BigInteger(num), value.bigIntegerValueExact());
+            } finally {
+                reader.close();
+            }
+        }
+    }
+
+    public void testUnknownFeature() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", true);
+        JsonReaderFactory factory = Json.createReaderFactory(config);
+        factory.createReader(new StringReader("{}"));
+        Map<String, ?> config1 = factory.getConfigInUse();
+        if (config1.size() > 0) {
+            fail("Shouldn't have any config in use");
+        }
+    }
+
+    public void testIllegalStateExcepton() throws Exception {
+        JsonReader reader = Json.createReader(new StringReader("{}"));
+        reader.readObject();
+        try {
+            reader.readObject();
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        reader.close();
+
+        reader = Json.createReader(new StringReader("[]"));
+        reader.readArray();
+        try {
+            reader.readArray();
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        reader.close();
+
+        reader = Json.createReader(new StringReader("{}"));
+        reader.read();
+        try {
+            reader.read();
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        reader.close();
+    }
+
+    static JsonObject readPerson() throws Exception {
+        Reader wikiReader = new InputStreamReader(JsonReaderTest.class.getResourceAsStream("/wiki.json"));
+        JsonReader reader = Json.createReader(wikiReader);
+        JsonValue value = reader.readObject();
+        reader.close();
+        return (JsonObject) value;
+    }
+
+    // JSONP-23 cached empty string is not reset
+    public void testEmptyStringUsingStandardBuffer() throws Throwable {
+        JsonReaderFactory factory = Json.createReaderFactory(null);
+        StringBuilder sb = new StringBuilder();
+        for(int i=0; i < 40000; i++) {
+            sb.append('a');
+            String name = sb.toString();
+            String str = "[1, \"\", \""+name+"\", \"\", \""+name+"\", \"\", 100]";
+            try {
+                JsonReader reader = factory.createReader(new StringReader(str));
+                JsonArray array = reader.readArray();
+                assertEquals(1, array.getInt(0));
+                assertEquals("", array.getString(1));
+                assertEquals(name, array.getString(2));
+                assertEquals("", array.getString(3));
+                assertEquals(name, array.getString(4));
+                assertEquals("", array.getString(5));
+                assertEquals(100, array.getInt(6));
+                reader.close();
+            } catch (Throwable t) {
+                throw new Throwable("Failed for name length="+i, t);
+            }
+        }
+    }
+
+    // JSONP-23 cached empty string is not reset
+    public void testEmptyStringUsingBuffers() throws Throwable {
+        for(int size=20; size < 500; size++) {
+            final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(size);
+            Map<String, Object> config = new HashMap<String, Object>() {{
+                put(BufferPool.class.getName(), bufferPool);
+            }};
+            JsonReaderFactory factory = Json.createReaderFactory(config);
+
+            StringBuilder sb = new StringBuilder();
+            for(int i=0; i < 1000; i++) {
+                sb.append('a');
+                String name = sb.toString();
+                String str = "[1, \"\", \""+name+"\", \"\", \""+name+"\", \"\", 100]";
+                try {
+                    JsonReader reader = factory.createReader(new StringReader(str));
+                    JsonArray array = reader.readArray();
+                    assertEquals(1, array.getInt(0));
+                    assertEquals("", array.getString(1));
+                    assertEquals(name, array.getString(2));
+                    assertEquals("", array.getString(3));
+                    assertEquals(name, array.getString(4));
+                    assertEquals("", array.getString(5));
+                    assertEquals(100, array.getInt(6));
+                    reader.close();
+                } catch (Throwable t) {
+                    throw new Throwable("Failed for buffer size="+size+" name length="+i, t);
+                }
+            }
+        }
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java
new file mode 100644
index 0000000..dd2702c
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.Json;
+import jakarta.json.JsonException;
+import jakarta.json.stream.JsonParser;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * JsonParser tests for sample files
+ *
+ * @author Jitendra Kotamraju
+ */
+public class JsonSamplesParsingTest extends TestCase {
+
+    public void testSampleFiles() {
+        String[] fileNames = {
+                "facebook.json", "facebook1.json", "facebook2.json",
+                "twitter.json"
+        };
+        for(String fileName: fileNames) {
+            try {
+                testSampleFile(fileName);
+            } catch(Exception e) {
+                throw new JsonException("Exception while parsing "+fileName, e);
+            }
+        }
+    }
+
+    private void testSampleFile(String fileName) {
+        Reader reader = new InputStreamReader(
+                JsonSamplesParsingTest.class.getResourceAsStream("/"+fileName), StandardCharsets.UTF_8);
+        JsonParser parser = null;
+        try {
+            parser = Json.createParser(reader);
+            while(parser.hasNext()) {
+                parser.next();
+            }
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+        }
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonStringTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonStringTest.java
new file mode 100644
index 0000000..6f7e7a9
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonStringTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import java.io.StringReader;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonStringTest extends TestCase {
+    public JsonStringTest(String testName) {
+        super(testName);
+    }
+
+    // tests JsonString#toString()
+    public void testToString() throws Exception {
+        escapedString("");
+        escapedString("abc");
+        escapedString("abc\f");
+        escapedString("abc\na");
+        escapedString("abc\tabc");
+        escapedString("abc\n\tabc");
+        escapedString("abc\n\tabc\r");
+        escapedString("\n\tabc\r");
+        escapedString("\bab\tb\rc\\\"\ftesting1234");
+        escapedString("\f\babcdef\tb\rc\\\"\ftesting1234");
+        escapedString("\u0000\u00ff");
+        escapedString("abc\"\\/abc");
+    }
+
+    public void testHashCode() {
+        String string1 = new String("a");
+        JsonString jsonString1 = Json.createValue(string1);
+        assertTrue(jsonString1.hashCode() == jsonString1.getString().hashCode());
+
+        String string2 = new String("a");
+        JsonString jsonString2 = Json.createValue(string2);
+
+        assertTrue(jsonString1.equals(jsonString2));
+        assertTrue(jsonString1.hashCode() == jsonString2.hashCode());
+    }
+
+    void escapedString(String str) throws Exception {
+        JsonArray exp = Json.createArrayBuilder().add(str).build();
+        String parseStr = "["+exp.get(0).toString()+"]";
+        JsonReader jr = Json.createReader(new StringReader(parseStr));
+        JsonArray got = jr.readArray();
+        assertEquals(exp, got);
+        jr.close();
+    }
+
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonValueTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonValueTest.java
new file mode 100644
index 0000000..af63746
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonValueTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author Lukas Jungmann
+ */
+public class JsonValueTest {
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetJsonObjectIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getJsonObject(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetJsonArrayIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getJsonArray(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetJsonNumberIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getJsonNumber(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetJsonStringIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getJsonString(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetStringIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getString(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetIntIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getInt(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayGetBooleanIdx() {
+        JsonValue.EMPTY_JSON_ARRAY.getBoolean(0);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void arrayIsNull() {
+        JsonValue.EMPTY_JSON_ARRAY.isNull(0);
+    }
+
+    @Test
+    public void arrayMethods() {
+        Assert.assertEquals(JsonValue.ValueType.ARRAY, JsonValue.EMPTY_JSON_ARRAY.getValueType());
+        Assert.assertEquals(Collections.<JsonObject>emptyList(), JsonValue.EMPTY_JSON_ARRAY.getValuesAs(JsonObject.class));
+        Assert.assertEquals(Collections.<String>emptyList(), JsonValue.EMPTY_JSON_ARRAY.getValuesAs(JsonString::getString));
+        Assert.assertEquals(true, JsonValue.EMPTY_JSON_ARRAY.getBoolean(0, true));
+        Assert.assertEquals(42, JsonValue.EMPTY_JSON_ARRAY.getInt(0, 42));
+        Assert.assertEquals("Sasek", JsonValue.EMPTY_JSON_ARRAY.getString(0, "Sasek"));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void arrayIsImmutable() {
+        JsonValue.EMPTY_JSON_ARRAY.add(JsonValue.EMPTY_JSON_OBJECT);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void objectGetString() {
+        JsonValue.EMPTY_JSON_OBJECT.getString("normalni string");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void objectGetInt() {
+        JsonValue.EMPTY_JSON_OBJECT.getInt("hledej cislo");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void objectGetBoolean() {
+        JsonValue.EMPTY_JSON_OBJECT.getBoolean("booo");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void objectIsNull() {
+        JsonValue.EMPTY_JSON_OBJECT.isNull("???");
+    }
+
+    @Test
+    public void objectMethods() {
+        Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonArray("pole"));
+        Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonObject("objekt"));
+        Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonNumber("cislo"));
+        Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonString("divnej string"));
+        
+        Assert.assertEquals("ja jo", JsonValue.EMPTY_JSON_OBJECT.getString("nejsem tu", "ja jo"));
+        Assert.assertEquals(false, JsonValue.EMPTY_JSON_OBJECT.getBoolean("najdes mne", false));
+        Assert.assertEquals(98, JsonValue.EMPTY_JSON_OBJECT.getInt("spatnej dotaz", 98));
+    }
+    
+    
+    @Test(expected = UnsupportedOperationException.class)
+    public void objectImmutable() {
+        JsonValue.EMPTY_JSON_OBJECT.put("klauni", JsonValue.EMPTY_JSON_ARRAY);
+    }
+
+    @Test
+    public void serialization() {
+        byte[] data = serialize(JsonValue.TRUE);
+        JsonValue value = deserialize(JsonValue.class, data);
+        Assert.assertEquals(JsonValue.TRUE, value);
+
+        data = serialize(JsonValue.FALSE);
+        value = deserialize(JsonValue.class, data);
+        Assert.assertEquals(JsonValue.FALSE, value);
+
+        data = serialize(JsonValue.NULL);
+        value = deserialize(JsonValue.class, data);
+        Assert.assertEquals(JsonValue.NULL, value);
+
+        data = serialize(JsonValue.EMPTY_JSON_ARRAY);
+        value = deserialize(JsonValue.class, data);
+        Assert.assertEquals(JsonValue.EMPTY_JSON_ARRAY, value);
+
+        data = serialize(JsonValue.EMPTY_JSON_OBJECT);
+        value = deserialize(JsonValue.class, data);
+        Assert.assertEquals(JsonValue.EMPTY_JSON_OBJECT, value);
+    }
+
+    private byte[] serialize(Object o) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(o);
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+        return baos.toByteArray();
+    }
+
+    private <T> T deserialize(Class<T> type, byte[] data) {
+        ByteArrayInputStream bais = new ByteArrayInputStream(data);
+        try (ObjectInputStream ois = new ObjectInputStream(bais)) {
+            return (T) ois.readObject();
+        } catch (IOException | ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/JsonWriterTest.java b/impl/src/test/java/org/glassfish/json/tests/JsonWriterTest.java
new file mode 100644
index 0000000..c097d4e
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/JsonWriterTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import jakarta.json.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Jitendra Kotamraju
+ */
+public class JsonWriterTest extends TestCase {
+    public JsonWriterTest(String testName) {
+        super(testName);
+    }
+
+    public void testObject() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.writeObject(Json.createObjectBuilder().build());
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("{}", writer.toString());
+    }
+
+    public void testEmptyObject() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write(JsonValue.EMPTY_JSON_OBJECT);
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("{}", writer.toString());
+    }
+
+    public void testArray() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.writeArray(Json.createArrayBuilder().build());
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("[]", writer.toString());
+    }
+
+    public void testEmptyArray() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write(JsonValue.EMPTY_JSON_ARRAY);
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("[]", writer.toString());
+    }
+
+    public void testNumber() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.writeArray(Json.createArrayBuilder().add(10).build());
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("[10]", writer.toString());
+    }
+
+    public void testDoubleNumber() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.writeArray(Json.createArrayBuilder().add(10.5).build());
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("[10.5]", writer.toString());
+    }
+
+    public void testArrayString() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.writeArray(Json.createArrayBuilder().add("string").build());
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("[\"string\"]", writer.toString());
+    }
+
+    public void testObjectAsValue() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write((JsonValue) (Json.createObjectBuilder().build()));
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("{}", writer.toString());
+    }
+
+    public void testNullValue() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write(JsonValue.NULL);
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("null", writer.toString());
+    }
+
+    public void testTrueValue() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write(JsonValue.TRUE);
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("true", writer.toString());
+    }
+
+    public void testFalseValue() throws Exception {
+        StringWriter writer = new StringWriter();
+        JsonWriter jsonWriter = Json.createWriter(writer);
+        jsonWriter.write(JsonValue.FALSE);
+        jsonWriter.close();
+        writer.close();
+
+        assertEquals("false", writer.toString());
+    }
+
+    public void testIllegalStateExcepton() throws Exception {
+        JsonObject obj = Json.createObjectBuilder().build();
+        JsonArray array = Json.createArrayBuilder().build();
+
+        JsonWriter writer = Json.createWriter(new StringWriter());
+        writer.writeObject(obj);
+        try {
+            writer.writeObject(obj);
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        writer.close();
+
+        writer = Json.createWriter(new StringWriter());
+        writer.writeArray(array);
+        try {
+            writer.writeArray(array);
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        writer.close();
+
+        writer = Json.createWriter(new StringWriter());
+        writer.write(array);
+        try {
+            writer.writeArray(array);
+        } catch (IllegalStateException expected) {
+            // no-op
+        }
+        writer.close();
+    }
+
+    public void testNoCloseWriteObjectToStream() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        JsonWriter writer = Json.createWriter(baos);
+        writer.write(Json.createObjectBuilder().build());
+        // not calling writer.close() intentionally
+        assertEquals("{}", baos.toString("UTF-8"));
+    }
+
+    public void testNoCloseWriteObjectToWriter() throws Exception {
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.write(Json.createObjectBuilder().build());
+        // not calling writer.close() intentionally
+        assertEquals("{}", sw.toString());
+    }
+
+    public void testNoCloseWriteArrayToStream() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        JsonWriter writer = Json.createWriter(baos);
+        writer.write(Json.createArrayBuilder().build());
+        // not calling writer.close() intentionally
+        assertEquals("[]", baos.toString("UTF-8"));
+    }
+
+    public void testNoCloseWriteArrayToWriter() throws Exception {
+        StringWriter sw = new StringWriter();
+        JsonWriter writer = Json.createWriter(sw);
+        writer.write(Json.createArrayBuilder().build());
+        // not calling writer.close() intentionally
+        assertEquals("[]", sw.toString());
+    }
+
+    public void testClose() throws Exception {
+        MyByteStream baos = new MyByteStream();
+        JsonWriter writer = Json.createWriter(baos);
+        writer.write(Json.createObjectBuilder().build());
+        writer.close();
+        assertEquals("{}", baos.toString("UTF-8"));
+        assertTrue(baos.isClosed());
+    }
+
+    private static final class MyByteStream extends ByteArrayOutputStream {
+        boolean closed;
+
+        boolean isClosed() {
+            return closed;
+        }
+
+        public void close() throws IOException {
+            super.close();
+            closed = true;
+        }
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/RFC7159Test.java b/impl/src/test/java/org/glassfish/json/tests/RFC7159Test.java
new file mode 100644
index 0000000..b4b7da8
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/RFC7159Test.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import org.junit.Test;
+import org.junit.BeforeClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonGenerator;
+import java.io.StringWriter;
+import java.io.StringReader;
+
+/**
+ * @author Kin-man Chung
+ */
+public class RFC7159Test {
+
+    @Test
+    public void testCreatValues() {
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        JsonArray array = builder.add(Json.createValue("someString"))
+                                 .add(Json.createValue(100))
+                                 .add(Json.createValue(12345.6789))
+                                 .build();
+        builder = Json.createArrayBuilder();
+        JsonArray expected = builder.add("someString")
+                                    .add(100)
+                                    .add(12345.6789)
+                                    .build();
+        assertEquals(expected, array);
+    }
+
+    @Test
+    public void testReadValues() {
+        JsonReader reader = Json.createReader(new StringReader("\"someString\""));
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        builder.add(reader.readValue());
+        reader = Json.createReader(new StringReader("100"));
+        builder.add(reader.readValue());
+        reader = Json.createReader(new StringReader("12345.6789"));
+        builder.add(reader.readValue());
+        JsonArray array = builder.build();
+        builder = Json.createArrayBuilder();
+        JsonArray expected = builder.add("someString")
+                                    .add(100)
+                                    .add(12345.6789)
+                                    .build();
+        assertEquals(expected, array);
+    }
+
+    @Test
+    public void testWriteValues() {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter writer = Json.createWriter(stringWriter);
+        writer.write(Json.createValue("someString"));
+        assertEquals("\"someString\"", stringWriter.toString());
+
+        stringWriter = new StringWriter();
+        writer = Json.createWriter(stringWriter);
+        writer.write(Json.createValue(100));
+        assertEquals("100", stringWriter.toString());
+
+        stringWriter = new StringWriter();
+        writer = Json.createWriter(stringWriter);
+        writer.write(Json.createValue(12345.6789));
+        assertEquals("12345.6789", stringWriter.toString());
+    }
+
+    @Test
+    public void testGeneratorValues() {
+        StringWriter stringWriter = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(stringWriter);
+        generator.write("someString").close();
+        assertEquals("\"someString\"", stringWriter.toString());
+
+        stringWriter = new StringWriter();
+        generator = Json.createGenerator(stringWriter);
+        generator.write(100).close();
+        assertEquals("100", stringWriter.toString());
+
+        stringWriter = new StringWriter();
+        generator = Json.createGenerator(stringWriter);
+        generator.write(12345.6789).close();
+        assertEquals("12345.6789", stringWriter.toString());
+    }
+}
diff --git a/impl/src/test/java/org/glassfish/json/tests/ToJsonTest.java b/impl/src/test/java/org/glassfish/json/tests/ToJsonTest.java
new file mode 100644
index 0000000..94893a2
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/ToJsonTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import org.junit.Test;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonValue;
+
+import org.glassfish.json.JsonUtil;
+
+import static org.junit.Assert.assertEquals;
+/**
+ * @author Kin-man Chung
+ */
+public class ToJsonTest {
+
+    @Test
+    public void testToJson() {
+        assertEquals(Json.createValue("someString"), JsonUtil.toJson("'someString'"));
+        assertEquals(Json.createValue("some'thing"), JsonUtil.toJson("'some\\'thing'"));
+        assertEquals(Json.createValue("some\"thing"), JsonUtil.toJson("'some\\\"thing'"));
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        JsonArray array = builder
+            .add(Json.createObjectBuilder()
+                .add("name", "John")
+                .add("age", 35)
+                .add("educations", Json.createArrayBuilder()
+                    .add("Gunn High")
+                    .add("UC Berkeley")))
+            .add(Json.createObjectBuilder()
+                .add("name", "Jane")
+                .add("educations", Json.createArrayBuilder()
+                    .add("Oxford")))
+            .build();
+         JsonValue expected = JsonUtil.toJson(
+             "[ { 'name': 'John', " +
+                 "'age': 35, " +
+                 "'educations': ['Gunn High', 'UC Berkeley'] }, " +
+              " { 'name': 'Jane', " +
+                 "'educations': ['Oxford']}]");
+          assertEquals(expected, array);
+    }
+}
+
diff --git a/impl/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java b/impl/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java
new file mode 100644
index 0000000..342d626
--- /dev/null
+++ b/impl/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.tests;
+
+import junit.framework.TestCase;
+
+import jakarta.json.*;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+import java.io.*;
+import java.net.URL;
+
+/**
+ * JsonParser Tests using twitter search API
+ *
+ * @author Jitendra Kotamraju
+ */
+public class TwitterSearchTest extends TestCase {
+
+    public void test() {
+        // dummy test so that junit doesn't complain
+    }
+
+    public void xtestStreamTwitter() throws Exception {
+        URL url = new URL("http://search.twitter.com/search.json?q=%23java&rpp=100");
+        InputStream is = url.openStream();
+        JsonParser parser = Json.createParser(is);
+
+        while(parser.hasNext()) {
+            Event e = parser.next();
+            if (e == Event.KEY_NAME) {
+                if (parser.getString().equals("from_user")) {
+                    parser.next();
+                    System.out.print(parser.getString());
+                    System.out.print(": ");
+                } else if (parser.getString().equals("text")) {
+                    parser.next();
+                    System.out.println(parser.getString());
+                    System.out.println("---------");
+                }
+            }
+        }
+        parser.close();
+	}
+
+    public void xtestObjectTwitter() throws Exception {
+        URL url = new URL("http://search.twitter.com/search.json?q=%23java&rpp=100");
+        InputStream is = url.openStream();
+        JsonReader rdr = Json.createReader(is);
+        JsonObject obj = rdr.readObject();
+        JsonArray results = obj.getJsonArray("results");
+        for(JsonObject result : results.getValuesAs(JsonObject.class)) {
+            System.out.print(result.get("from_user"));
+            System.out.print(": ");
+            System.out.println(result.get("text"));
+            System.out.println("-----------");
+        }
+        rdr.close();
+    }
+
+}
diff --git a/impl/src/test/resources/facebook.json b/impl/src/test/resources/facebook.json
new file mode 100644
index 0000000..1a82909
--- /dev/null
+++ b/impl/src/test/resources/facebook.json
@@ -0,0 +1,668 @@
+{
+   "data": [
+      {
+         "id": "123_1",
+         "from": {
+            "name": "Name 1",
+            "id": "1"
+         },
+         "message": "Dummy message Name 1",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for iPhone",
+            "namespace": "fbiphone",
+            "id": "12345"
+         },
+         "created_time": "2013-07-26T22:33:09+0000",
+         "updated_time": "2013-07-26T22:33:09+0000"
+      },
+      {
+         "id": "123_2",
+         "from": {
+            "category": "Author",
+            "name": "Name 2",
+            "id": "2"
+         },
+         "message": "Dummy message Name 2",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/9879asd/987645"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:33:03+0000",
+         "updated_time": "2013-07-26T22:33:03+0000"
+      },
+      {
+         "id": "123_3",
+         "from": {
+            "category": "Community",
+            "name": "Group 1",
+            "id": "3"
+         },
+         "message": "Dummy message Group 1",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/68548"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:33:00+0000",
+         "updated_time": "2013-07-26T22:33:00+0000"
+      },
+      {
+         "id": "123_4",
+         "from": {
+            "category": "Bank/financial institution",
+            "name": "Group 2",
+            "id": "4"
+         },
+         "message": "Dummy message Group 2",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/1234"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Seesmic",
+            "id": "34567"
+         },
+         "created_time": "2013-07-26T22:32:58+0000",
+         "updated_time": "2013-07-26T22:32:58+0000"
+      },
+      {
+         "id": "123_5",
+         "from": {
+            "name": "Person 5",
+            "id": "5"
+         },
+         "message": "Dummy message Person 5",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:32:55+0000",
+         "updated_time": "2013-07-26T22:32:55+0000"
+      },
+      {
+         "id": "123_6",
+         "from": {
+            "name": "Person 6",
+            "id": "6"
+         },
+         "message": "Dummy message Person 6",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:32:46+0000",
+         "updated_time": "2013-07-26T22:32:46+0000"
+      },
+      {
+         "id": "123_7",
+         "from": {
+            "category": "Community",
+            "name": "\u0e2a\u0e19Group 7",
+            "id": "7"
+         },
+         "message": "Dummy message Group 7",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/7"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "DoubleQ",
+            "namespace": "doubleqapp",
+            "id": "98765"
+         },
+         "created_time": "2013-07-26T22:32:41+0000",
+         "updated_time": "2013-07-26T22:32:41+0000"
+      },
+      {
+         "id": "123_8",
+         "from": {
+            "name": "Person 8",
+            "id": "8"
+         },
+         "message": "Dummy message Person 8",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:32:36+0000",
+         "updated_time": "2013-07-26T22:32:36+0000"
+      },
+      {
+         "id": "123_9",
+         "from": {
+            "category": "Bank/financial institution",
+            "name": "Group 9",
+            "id": "9"
+         },
+         "message": "Dummy message Group 9",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/9"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Status Shuffle",
+            "namespace": "status-shuffle",
+            "id": "87654"
+         },
+         "created_time": "2013-07-26T22:32:30+0000",
+         "updated_time": "2013-07-26T22:32:30+0000"
+      },
+      {
+         "id": "123_10",
+         "from": {
+            "name": "Person 10",
+            "id": "10"
+         },
+         "message": "Dummy message Person 10",
+         "picture": "https://picture.url/123/10.jpg",
+         "link": "https://custom.url/123/10",
+         "name": "Dummy page name - Person 10",
+         "caption": "www.page.com",
+         "description": "Some random description",
+         "icon": "https://icon.url/123/10.ico",
+         "privacy": {
+            "value": ""
+         },
+         "type": "link",
+         "created_time": "2013-07-26T22:32:14+0000",
+         "updated_time": "2013-07-26T22:32:14+0000"
+      },
+      {
+         "id": "123_11",
+         "from": {
+            "category": "Community",
+            "name": "Group 11",
+            "id": "11"
+         },
+         "message": "Dummy message Group 11",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/11"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "twitterfeed",
+            "id": "654987"
+         },
+         "created_time": "2013-07-26T22:32:13+0000",
+         "updated_time": "2013-07-26T22:32:13+0000"
+      },
+      {
+         "id": "123_12",
+         "from": {
+            "category": "Entertainer",
+            "name": "Person 12",
+            "id": "12"
+         },
+         "message": "Dummy message Person 12",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/12"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:32:10+0000",
+         "updated_time": "2013-07-26T22:32:10+0000"
+      },
+      {
+         "id": "123_13",
+         "from": {
+            "category": "Health/beauty",
+            "name": "Group 13",
+            "id": "13"
+         },
+         "message": "Dummy message Group 13",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/13"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "twitterfeed",
+            "id": "654987"
+         },
+         "created_time": "2013-07-26T22:32:07+0000",
+         "updated_time": "2013-07-26T22:32:07+0000"
+      },
+      {
+         "id": "123_14",
+         "from": {
+            "name": "Person 14",
+            "id": "14"
+         },
+         "message": "Dummy message Person 14",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:31:53+0000",
+         "updated_time": "2013-07-26T22:31:53+0000"
+      },
+      {
+         "id": "123_15",
+         "from": {
+            "category": "Community",
+            "name": "Group 15",
+            "id": "15"
+         },
+         "message": "Dummy message Group 15",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/15"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:31:40+0000",
+         "updated_time": "2013-07-26T22:31:40+0000"
+      },
+      {
+         "id": "123_16",
+         "from": {
+            "name": "Person 16",
+            "id": "16"
+         },
+         "message": "Dummy message Person 16",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Nokia",
+            "id": "34567"
+         },
+         "created_time": "2013-07-26T22:31:39+0000",
+         "updated_time": "2013-07-26T22:31:39+0000"
+      },
+      {
+         "id": "123_17",
+         "from": {
+            "name": "Person 17",
+            "id": "17"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 16",
+                  "id": "16"
+               }
+            ]
+         },
+         "with_tags": {
+            "data": [
+               {
+                  "name": "Person 16",
+                  "id": "16"
+               }
+            ]
+         },
+         "message": "Dummy message Person 16",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for iPhone",
+            "namespace": "fbiphone",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:31:38+0000",
+         "updated_time": "2013-07-26T22:31:38+0000"
+      },
+      {
+         "id": "123_18",
+         "from": {
+            "category": "Bank/financial institution",
+            "name": "Group 18",
+            "id": "18"
+         },
+         "message": "Dummy message Group 18",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/18"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Status Shuffle",
+            "namespace": "status-shuffle",
+            "id": "876543"
+         },
+         "created_time": "2013-07-26T22:31:37+0000",
+         "updated_time": "2013-07-26T22:31:37+0000"
+      },
+      {
+         "id": "123_19",
+         "from": {
+            "category": "Community",
+            "name": "Group 19",
+            "id": "19"
+         },
+         "message": "Dummy message Group 19",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/19"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:31:36+0000",
+         "updated_time": "2013-07-26T22:31:36+0000"
+      },
+      {
+         "id": "123_20",
+         "from": {
+            "name": "Person 20",
+            "id": "20"
+         },
+         "message": "Dummy message Person 20",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:31:36+0000",
+         "updated_time": "2013-07-26T22:31:36+0000"
+      },
+      {
+         "id": "123_21",
+         "from": {
+            "category": "Website",
+            "name": "Page 21",
+            "id": "21"
+         },
+         "message": "Dummy message Page 21",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/21"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "dlvr.it",
+            "namespace": "dlvr_it",
+            "id": "456321"
+         },
+         "created_time": "2013-07-26T22:31:34+0000",
+         "updated_time": "2013-07-26T22:31:34+0000"
+      },
+      {
+         "id": "123_22",
+         "from": {
+            "name": "Person 22",
+            "id": "22"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 99",
+                  "id": "99"
+               },
+               {
+                  "name": "Person 98",
+                  "id": "98"
+               },
+               {
+                  "name": "Person 97",
+                  "id": "97"
+               },
+               {
+                  "name": "Person 96",
+                  "id": "96"
+               },
+               {
+                  "name": "Person 95",
+                  "id": "95"
+               },
+               {
+                  "name": "Person 94",
+                  "id": "94"
+               },
+               {
+                  "name": "Person 93",
+                  "id": "93"
+               },
+               {
+                  "name": "Person 92",
+                  "id": "92"
+               }
+            ]
+         },
+         "message": "Dummy message Person 22",
+         "message_tags": {
+            "50": [
+               {
+                  "id": "99",
+                  "name": "Person 99",
+                  "type": "user",
+                  "offset": 50,
+                  "length": 13
+               }
+            ],
+            "64": [
+               {
+                  "id": "98",
+                  "name": "Person 98",
+                  "type": "user",
+                  "offset": 64,
+                  "length": 25
+               }
+            ],
+            "90": [
+               {
+                  "id": "97",
+                  "name": "Person 97",
+                  "type": "user",
+                  "offset": 90,
+                  "length": 11
+               }
+            ],
+            "102": [
+               {
+                  "id": "96",
+                  "name": "Person 96",
+                  "type": "user",
+                  "offset": 102,
+                  "length": 14
+               }
+            ],
+            "117": [
+               {
+                  "id": "95",
+                  "name": "Person 95",
+                  "type": "user",
+                  "offset": 117,
+                  "length": 17
+               }
+            ],
+            "135": [
+               {
+                  "id": "94",
+                  "name": "Person 94",
+                  "type": "user",
+                  "offset": 135,
+                  "length": 13
+               }
+            ],
+            "149": [
+               {
+                  "id": "93",
+                  "name": "Person 93",
+                  "type": "user",
+                  "offset": 149,
+                  "length": 21
+               }
+            ],
+            "171": [
+               {
+                  "id": "92",
+                  "name": "Person 92",
+                  "type": "user",
+                  "offset": 171,
+                  "length": 11
+               }
+            ]
+         },
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:31:33+0000",
+         "updated_time": "2013-07-26T22:31:33+0000"
+      },
+      {
+         "id": "123_23",
+         "from": {
+            "name": "Person 23",
+            "id": "23"
+         },
+         "message": "Dummy message Person 23",
+         "picture": "https://custom.url/123/23.jpg",
+         "link": "https://custom.url/123/23",
+         "name": "Person 23 Name",
+         "caption": " ",
+         "description": " ",
+         "icon": "https://custom.url/123/23.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "link",
+         "application": {
+            "name": "RSS Graffiti",
+            "namespace": "rssgraffiti",
+            "id": "357951"
+         },
+         "created_time": "2013-07-26T22:31:29+0000",
+         "updated_time": "2013-07-26T22:31:29+0000"
+      },
+      {
+         "id": "123_24",
+         "from": {
+            "name": "Person 24",
+            "id": "24"
+         },
+         "message": "Dummy message person 24",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:31:29+0000",
+         "updated_time": "2013-07-26T22:31:29+0000"
+      },
+      {
+         "id": "123_25",
+         "from": {
+            "name": "Person 25",
+            "id": "25"
+         },
+         "message": "Dummy message Person 25",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:31:22+0000",
+         "updated_time": "2013-07-26T22:31:22+0000"
+      }
+   ],
+   "paging": {
+      "previous": "https://custom.url/previous",
+      "next": "https://custom.url/next"
+   }
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/facebook1.json b/impl/src/test/resources/facebook1.json
new file mode 100644
index 0000000..5016652
--- /dev/null
+++ b/impl/src/test/resources/facebook1.json
@@ -0,0 +1,911 @@
+{
+   "data": [
+      {
+         "id": "123_1",
+         "from": {
+            "category": "Bank/financial institution",
+            "name": "Group 1",
+            "id": "1"
+         },
+         "message": "Dummy message Group 1",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/1"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Seesmic",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:45:54+0000",
+         "updated_time": "2013-07-26T22:45:54+0000"
+      },
+      {
+         "id": "123_2",
+         "from": {
+            "name": "Person 2",
+            "id": "2"
+         },
+         "message": "Dummy message Person 2",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:52+0000",
+         "updated_time": "2013-07-26T22:45:52+0000"
+      },
+      {
+         "id": "123_3",
+         "from": {
+            "category": "Outdoor gear/sporting goods",
+            "category_list": [
+               {
+                  "id": "2231",
+                  "name": "Outdoor Gear/Sporting Goods"
+               }
+            ],
+            "name": "Store 3",
+            "id": "3"
+         },
+         "message": "https://shop.url/123/3",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/3"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:51+0000",
+         "updated_time": "2013-07-26T22:45:51+0000"
+      },
+      {
+         "id": "123_4",
+         "from": {
+            "name": "Person 4",
+            "id": "4"
+         },
+         "message": "Dummy message Person 4",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:45:50+0000",
+         "updated_time": "2013-07-26T22:45:50+0000"
+      },
+      {
+         "id": "123_5",
+         "from": {
+            "name": "Person 5",
+            "id": "5"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 2",
+                  "id": "2"
+               },
+               {
+                  "name": "Person 99",
+                  "id": "99"
+               },
+               {
+                  "name": "Person 98",
+                  "id": "98"
+               }
+            ]
+         },
+         "message": "Dummy message Person 5",
+         "message_tags": {
+            "38": [
+               {
+                  "id": "2",
+                  "name": "Person 2",
+                  "type": "user",
+                  "offset": 38,
+                  "length": 13
+               }
+            ],
+            "53": [
+               {
+                  "id": "99",
+                  "name": "Person 99",
+                  "type": "user",
+                  "offset": 53,
+                  "length": 17
+               }
+            ],
+            "73": [
+               {
+                  "id": "98",
+                  "name": "Person 98",
+                  "type": "user",
+                  "offset": 73,
+                  "length": 18
+               }
+            ]
+         },
+         "story": "Dummy story Person 5",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "97",
+                  "name": "Person 97",
+                  "offset": 0,
+                  "length": 15,
+                  "type": "user"
+               }
+            ],
+            "23": [
+               {
+                  "id": "96",
+                  "name": "Person 96",
+                  "offset": 23,
+                  "length": 18,
+                  "type": "user"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/5.jpg",
+         "link": "https://profile.url/123/5",
+         "name": "Person 5 Photos",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Person 5",
+               "href": "https://profile.url/123/5"
+            }
+         ],
+         "icon": "https://custom.url/123/5.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "123",
+         "application": {
+            "name": "Photos",
+            "id": "1234"
+         },
+         "created_time": "2013-07-26T22:45:45+0000",
+         "updated_time": "2013-07-26T22:45:45+0000"
+      },
+      {
+         "id": "123_6",
+         "from": {
+            "category": "Public figure",
+            "name": "Person 6",
+            "id": "6"
+         },
+         "message": "Dummy message Person 6",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/6"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:44+0000",
+         "updated_time": "2013-07-26T22:45:44+0000"
+      },
+      {
+         "id": "123_7",
+         "from": {
+            "name": "Person 7",
+            "id": "7"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 89",
+                  "id": "89"
+               }
+            ]
+         },
+         "message": "Dummy message Person 7",
+         "message_tags": {
+            "16": [
+               {
+                  "id": "987",
+                  "name": "Person 987",
+                  "type": "user",
+                  "offset": 16,
+                  "length": 18
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/7.jpg",
+         "link": "https://custom.url/123/7",
+         "source": "https://source.url/123/7",
+         "name": "Link name person 7",
+         "description": "Description Person 7",
+         "icon": "https://custom.url/123/7.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "video",
+         "created_time": "2013-07-26T22:45:44+0000",
+         "updated_time": "2013-07-26T22:45:44+0000"
+      },
+      {
+         "id": "123_8",
+         "from": {
+            "name": "Person 8",
+            "id": "8"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 1",
+                  "id": "1"
+               },
+               {
+                  "name": "Person 2",
+                  "id": "2"
+               },
+               {
+                  "name": "Person 3",
+                  "id": "3"
+               },
+               {
+                  "name": "Person 4",
+                  "id": "4"
+               },
+               {
+                  "name": "Person 5",
+                  "id": "5"
+               },
+               {
+                  "name": "Person 99",
+                  "id": "99"
+               },
+               {
+                  "name": "Person 98",
+                  "id": "98"
+               },
+               {
+                  "name": "Person 69",
+                  "id": "69"
+               }
+            ]
+         },
+         "message": "Dummy message Person 8",
+         "message_tags": {
+            "77": [
+               {
+                  "id": "1",
+                  "name": "Person 1",
+                  "type": "user",
+                  "offset": 77,
+                  "length": 26
+               }
+            ],
+            "104": [
+               {
+                  "id": "2",
+                  "name": "Person 2",
+                  "type": "user",
+                  "offset": 104,
+                  "length": 11
+               }
+            ],
+            "116": [
+               {
+                  "id": "3",
+                  "name": "Person 3",
+                  "type": "user",
+                  "offset": 116,
+                  "length": 13
+               }
+            ],
+            "130": [
+               {
+                  "id": "4",
+                  "name": "Person 4",
+                  "type": "user",
+                  "offset": 130,
+                  "length": 22
+               }
+            ],
+            "153": [
+               {
+                  "id": "5",
+                  "name": "Person 5",
+                  "type": "user",
+                  "offset": 153,
+                  "length": 20
+               }
+            ],
+            "174": [
+               {
+                  "id": "99",
+                  "name": "Person 99",
+                  "type": "user",
+                  "offset": 174,
+                  "length": 12
+               }
+            ],
+            "187": [
+               {
+                  "id": "98",
+                  "name": "Person 98",
+                  "type": "user",
+                  "offset": 187,
+                  "length": 16
+               }
+            ],
+            "204": [
+               {
+                  "id": "69",
+                  "name": "Person 69",
+                  "type": "user",
+                  "offset": 204,
+                  "length": 21
+               }
+            ]
+         },
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:41+0000",
+         "updated_time": "2013-07-26T22:45:41+0000"
+      },
+      {
+         "id": "123_9",
+         "from": {
+            "name": "Person 9",
+            "id": "9"
+         },
+         "message": "Dummy message Person 9",
+         "story": "Story Person 9",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "8",
+                  "name": "Person 8",
+                  "offset": 0,
+                  "length": 10,
+                  "type": "user"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/9.jpg",
+         "link": "https://custom.url/123/9",
+         "icon": "https://custom.url/123/9.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_8",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "34567"
+         },
+         "created_time": "2013-07-26T22:45:33+0000",
+         "updated_time": "2013-07-26T22:45:33+0000"
+      },
+      {
+         "id": "123_9",
+         "from": {
+            "name": "Person 9",
+            "id": "9"
+         },
+         "message": "Dummy message Person 9",
+         "story": "Story Person 9",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "6",
+                  "name": "Person 6",
+                  "offset": 0,
+                  "length": 13,
+                  "type": "user"
+               }
+            ],
+            "21": [
+               {
+                  "id": "7",
+                  "name": "Person 7",
+                  "offset": 21,
+                  "length": 25,
+                  "type": "user"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/9.jpg",
+         "link": "https://custom.url/123/9",
+         "name": "Link Person 9",
+         "caption": "Caption Person 9",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Person 9",
+               "href": "https://custom.url/123/9"
+            }
+         ],
+         "icon": "https://custom.url/123/9.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_9",
+         "application": {
+            "name": "Links",
+            "id": "951753"
+         },
+         "created_time": "2013-07-26T22:45:33+0000",
+         "updated_time": "2013-07-26T22:45:33+0000"
+      },
+      {
+         "id": "123_10",
+         "from": {
+            "name": "Person 10",
+            "id": "10"
+         },
+         "message": "Dummy message Person 10",
+         "story": "Story Person 10",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "3",
+                  "name": "Person 3",
+                  "offset": 0,
+                  "length": 27,
+                  "type": "user"
+               }
+            ],
+            "35": [
+               {
+                  "id": "79",
+                  "name": "Page 79",
+                  "offset": 35,
+                  "length": 4,
+                  "type": "page"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/10.jpg",
+         "link": "https://link.url/123/10",
+         "name": "Timeline Photos",
+         "caption": "Caption link Person 10",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Page 79",
+               "href": "https://custom.url/123/79"
+            }
+         ],
+         "icon": "https://custom.url/123/10.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_10",
+         "application": {
+            "name": "Photos",
+            "id": "3456"
+         },
+         "created_time": "2013-07-26T22:45:31+0000",
+         "updated_time": "2013-07-26T22:45:31+0000"
+      },
+      {
+         "id": "123_11",
+         "from": {
+            "name": "Person 11",
+            "id": "11"
+         },
+         "message": "Dummy message Person 11",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for iPhone",
+            "namespace": "fbiphone",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:45:25+0000",
+         "updated_time": "2013-07-26T22:45:25+0000"
+      },
+      {
+         "id": "123_12",
+         "from": {
+            "name": "Person 12",
+            "id": "12"
+         },
+         "message": "Dummy message Person 12",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "3456"
+         },
+         "created_time": "2013-07-26T22:45:20+0000",
+         "updated_time": "2013-07-26T22:45:20+0000"
+      },
+      {
+         "id": "123_13",
+         "from": {
+            "name": "Person 13",
+            "id": "13"
+         },
+         "message": "Dummy message person 13",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:18+0000",
+         "updated_time": "2013-07-26T22:45:18+0000"
+      },
+      {
+         "id": "123_14",
+         "from": {
+            "name": "Person 14",
+            "id": "14"
+         },
+         "message": "Dummy message Person 14",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:17+0000",
+         "updated_time": "2013-07-26T22:45:17+0000"
+      },
+      {
+         "id": "123_15",
+         "from": {
+            "name": "Person 15",
+            "id": "15"
+         },
+         "message": "Dummy message Person 15",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:16+0000",
+         "updated_time": "2013-07-26T22:45:16+0000"
+      },
+      {
+         "id": "123_16",
+         "from": {
+            "name": "Person 16",
+            "id": "16"
+         },
+         "message": "Dummy message Person 16",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "3456"
+         },
+         "created_time": "2013-07-26T22:45:15+0000",
+         "updated_time": "2013-07-26T22:45:15+0000"
+      },
+      {
+         "id": "123_17",
+         "from": {
+            "name": "Person 17",
+            "id": "17"
+         },
+         "message": "Dummy message Person 17",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:45:12+0000",
+         "updated_time": "2013-07-26T22:45:12+0000"
+      },
+      {
+         "id": "123_18",
+         "from": {
+            "name": "Person 18",
+            "id": "18"
+         },
+         "message": "Dummy message Person 18",
+         "picture": "https://custom.url/123/18.jpg",
+         "link": "https://link.url/123/18",
+         "icon": "https://custom.url/123/18.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_18",
+         "created_time": "2013-07-26T22:45:11+0000",
+         "updated_time": "2013-07-26T22:45:11+0000"
+      },
+      {
+         "id": "123_19",
+         "from": {
+            "name": "Person 19",
+            "id": "19"
+         },
+         "message": "Dummy message Person 19",
+         "story": "Person 19 Story",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "19",
+                  "name": "Person 19",
+                  "offset": 0,
+                  "length": 13,
+                  "type": "user"
+               }
+            ],
+            "21": [
+               {
+                  "id": "75",
+                  "name": "Person 75",
+                  "offset": 21,
+                  "length": 31,
+                  "type": "page"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/19.jpg",
+         "link": "https://link.url/123/19",
+         "name": "Person 19 Photos",
+         "caption": "Caption Person 19",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Group 89",
+               "href": "https://custom.url/123/19"
+            }
+         ],
+         "icon": "https://custom.url/123/19.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_19",
+         "application": {
+            "name": "Photos",
+            "id": "12375"
+         },
+         "created_time": "2013-07-26T22:45:07+0000",
+         "updated_time": "2013-07-26T22:45:07+0000"
+      },
+      {
+         "id": "123_20",
+         "from": {
+            "name": "Person 20",
+            "id": "20"
+         },
+         "message": "Dummy message Person 20",
+         "story": "Person 20 shared a photo.",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "20",
+                  "name": "Person 20",
+                  "offset": 0,
+                  "length": 21,
+                  "type": "user"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/20.jpg",
+         "link": "https://link.url/123/23",
+         "name": "Person 20's Photos",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Person 20",
+               "href": "https://custom.url/123/20"
+            }
+         ],
+         "icon": "https://custom.url/123/20.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_20",
+         "application": {
+            "name": "Photos",
+            "id": "45678"
+         },
+         "created_time": "2013-07-26T22:45:00+0000",
+         "updated_time": "2013-07-26T22:45:00+0000"
+      },
+      {
+         "id": "123_21",
+         "from": {
+            "name": "Person 21",
+            "id": "21"
+         },
+         "message": "Dummy message Person 21",
+         "story": "Person 21 shared something.",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "21",
+                  "name": "Person 21",
+                  "offset": 0,
+                  "length": 18,
+                  "type": "user"
+               }
+            ],
+            "26": [
+               {
+                  "id": "67",
+                  "name": "Group 67",
+                  "offset": 26,
+                  "length": 30,
+                  "type": "page"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/21.jpg",
+         "link": "https://link.url/123/21",
+         "name": "Timeline Photos",
+         "caption": "Person 21 Caption",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Group 75",
+               "href": "https://custom.url/123/75"
+            }
+         ],
+         "icon": "https://custom.url/123/21.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_21",
+         "application": {
+            "name": "Photos",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:44:58+0000",
+         "updated_time": "2013-07-26T22:44:58+0000"
+      },
+      {
+         "id": "123_22",
+         "from": {
+            "name": "Person 22",
+            "id": "22"
+         },
+         "message": "Dummy message Person 22",
+         "story": "Person 22 shared photo.",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "22",
+                  "name": "Person 22",
+                  "offset": 0,
+                  "length": 21,
+                  "type": "user"
+               }
+            ],
+            "29": [
+               {
+                  "id": "88",
+                  "name": "Person 88",
+                  "offset": 29,
+                  "length": 12,
+                  "type": "page"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/22.jpg",
+         "link": "https://link.url/123/22",
+         "name": "Timeline Photos",
+         "caption": "Person 22 caption",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Person 45",
+               "href": "https://custom.url/123/45"
+            }
+         ],
+         "icon": "https://custom.url/123/22.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_22",
+         "application": {
+            "name": "Photos",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:44:58+0000",
+         "updated_time": "2013-07-26T22:44:58+0000"
+      },
+      {
+         "id": "123_23",
+         "from": {
+            "category": "Teacher",
+            "name": "Person 23",
+            "id": "23"
+         },
+         "message": "Dummy message Person 23",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/23"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:44:57+0000",
+         "updated_time": "2013-07-26T22:44:57+0000"
+      },
+      {
+         "id": "24_10151496285490925",
+         "from": {
+            "name": "Person 24",
+            "id": "24"
+         },
+         "message": "Dummy message Person 24",
+         "story": "Person 24 shared photo.",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "24",
+                  "name": "Person 24",
+                  "offset": 0,
+                  "length": 30,
+                  "type": "user"
+               }
+            ],
+            "38": [
+               {
+                  "id": "86",
+                  "name": "Person 86",
+                  "offset": 38,
+                  "length": 14,
+                  "type": "user"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/24.jpg",
+         "link": "https://link.url/123/23",
+         "name": "Person 86 Photos",
+         "caption": "Person 24 Caption",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Person 86",
+               "href": "https://custom.url/123/86"
+            }
+         ],
+         "icon": "https://custom.url/123/24.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_24",
+         "application": {
+            "name": "Links",
+            "id": "654321"
+         },
+         "created_time": "2013-07-26T22:44:50+0000",
+         "updated_time": "2013-07-26T22:44:50+0000"
+      }
+   ],
+   "paging": {
+      "previous": "https://custom.url/previous",
+      "next": "https://custom.url/next"
+   }
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/facebook2.json b/impl/src/test/resources/facebook2.json
new file mode 100644
index 0000000..b57b1ed
--- /dev/null
+++ b/impl/src/test/resources/facebook2.json
@@ -0,0 +1,555 @@
+{
+   "data": [
+      {
+         "id": "123_1",
+         "from": {
+            "name": "Person 1",
+            "id": "1"
+         },
+         "message": "Dummy message Person 1",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:57:09+0000",
+         "updated_time": "2013-07-26T22:57:09+0000"
+      },
+      {
+         "id": "123_2",
+         "from": {
+            "name": "Person 2",
+            "id": "2"
+         },
+         "message": "Dummy message Person 2",
+         "picture": "https://custom.url/123/2.jpg",
+         "link": "https://link.url/123/2",
+         "source": "https://source.url/123/2",
+         "name": "Person 2",
+         "icon": "https://custom.url/123/2.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "video",
+         "application": {
+            "name": "Share_bookmarklet",
+            "id": "234567"
+         },
+         "created_time": "2013-07-26T22:57:08+0000",
+         "updated_time": "2013-07-26T22:57:08+0000"
+      },
+      {
+         "id": "123_3",
+         "from": {
+            "category": "Community",
+            "name": "Group 3",
+            "id": "3"
+         },
+         "message": "Dummy message Group 3",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/3"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:57:05+0000",
+         "updated_time": "2013-07-26T22:57:05+0000"
+      },
+      {
+         "id": "123_4",
+         "from": {
+            "name": "Person 4",
+            "id": "4"
+         },
+         "message": "Dummy message Person 4",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:57:00+0000",
+         "updated_time": "2013-07-26T22:57:00+0000"
+      },
+      {
+         "id": "123_5",
+         "from": {
+            "name": "Person 5",
+            "id": "5"
+         },
+         "message": "Dummy message Person 5",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:56:58+0000",
+         "updated_time": "2013-07-26T22:56:58+0000"
+      },
+      {
+         "id": "123_6",
+         "from": {
+            "name": "Person 6",
+            "id": "6"
+         },
+         "message": "Dummy message Person 6",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for iPhone",
+            "namespace": "fbiphone",
+            "id": "23456"
+         },
+         "created_time": "2013-07-26T22:56:54+0000",
+         "updated_time": "2013-07-26T22:56:54+0000"
+      },
+      {
+         "id": "123_7",
+         "from": {
+            "name": "Person 7",
+            "id": "7"
+         },
+         "message": "Dummy message Person 7",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:56:47+0000",
+         "updated_time": "2013-07-26T22:56:47+0000"
+      },
+      {
+         "id": "123_8",
+         "from": {
+            "name": "Person 8",
+            "id": "8"
+         },
+         "message": "Dummy message Person 8",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:56:45+0000",
+         "updated_time": "2013-07-26T22:56:45+0000"
+      },
+      {
+         "id": "123_9",
+         "from": {
+            "name": "Person 9",
+            "id": "9"
+         },
+         "message": "Dummy message Person 9",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:56:44+0000",
+         "updated_time": "2013-07-26T22:56:44+0000"
+      },
+      {
+         "id": "123_10",
+         "from": {
+            "name": "Person 10",
+            "id": "10"
+         },
+         "message": "Dummy message Person 10",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:56:36+0000",
+         "updated_time": "2013-07-26T22:56:36+0000"
+      },
+      {
+         "id": "123_11",
+         "from": {
+            "name": "Person 11",
+            "id": "11"
+         },
+         "message": "Dummy message Person 11",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:56:32+0000",
+         "updated_time": "2013-07-26T22:56:32+0000"
+      },
+      {
+         "id": "123_12",
+         "from": {
+            "name": "Person 12",
+            "id": "12"
+         },
+         "message": "Dummy message Person 12",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:56:27+0000",
+         "updated_time": "2013-07-26T22:56:27+0000"
+      },
+      {
+         "id": "123_13",
+         "from": {
+            "category": "Bank/financial institution",
+            "name": "Group 13",
+            "id": "13"
+         },
+         "message": "Dummy message Group 13",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://link.url/123/23"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "PostCron",
+            "namespace": "postcron",
+            "id": "159357"
+         },
+         "created_time": "2013-07-26T22:56:26+0000",
+         "updated_time": "2013-07-26T22:56:26+0000"
+      },
+      {
+         "id": "123_14",
+         "from": {
+            "name": "Person 14",
+            "id": "14"
+         },
+         "to": {
+            "data": [
+               {
+                  "name": "Person 99",
+                  "id": "99"
+               },
+               {
+                  "name": "Person 98",
+                  "id": "98"
+               }
+            ]
+         },
+         "message": "Dummy message Person 14",
+         "message_tags": {
+            "0": [
+               {
+                  "id": "99",
+                  "name": "Person 99",
+                  "type": "user",
+                  "offset": 0,
+                  "length": 21
+               }
+            ],
+            "159": [
+               {
+                  "id": "98",
+                  "name": "Person 98",
+                  "type": "user",
+                  "offset": 159,
+                  "length": 26
+               }
+            ]
+         },
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:56:16+0000",
+         "updated_time": "2013-07-26T22:56:16+0000"
+      },
+      {
+         "id": "123_15",
+         "from": {
+            "name": "Person 15",
+            "id": "15"
+         },
+         "message": "Dummy message Person 15",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:56:15+0000",
+         "updated_time": "2013-07-26T22:56:15+0000"
+      },
+      {
+         "id": "123_16",
+         "from": {
+            "category": "Just for fun",
+            "name": "Group 16",
+            "id": "16"
+         },
+         "message": "Dummy message Group 16",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/16"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:56:14+0000",
+         "updated_time": "2013-07-26T22:56:14+0000",
+         "likes": {
+            "data": [
+               {
+                  "id": "97",
+                  "name": "Person 97"
+               }
+            ],
+            "paging": {
+               "cursors": {
+                  "after": "MTAwMDAzMDQzNDM5MDQ5",
+                  "before": "MTAwMDAzMDQzNDM5MDQ5"
+               }
+            }
+         }
+      },
+      {
+         "id": "123_17",
+         "from": {
+            "category": "Community",
+            "name": "Group 17",
+            "id": "17"
+         },
+         "message": "Dummy message Group 17",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/17"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Pages Manager for Android",
+            "namespace": "fbpagemgr_android",
+            "id": "951753"
+         },
+         "created_time": "2013-07-26T22:56:13+0000",
+         "updated_time": "2013-07-26T22:56:13+0000"
+      },
+      {
+         "id": "123_18",
+         "from": {
+            "name": "Person 18",
+            "id": "18"
+         },
+         "message": "Dummy message Person 18",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Facebook for iPad",
+            "namespace": "fbipad_",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:56:07+0000",
+         "updated_time": "2013-07-26T22:56:07+0000"
+      },
+      {
+         "id": "123_19",
+         "from": {
+            "name": "Person 19",
+            "id": "19"
+         },
+         "message": "Dummy message Person 19",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:56:04+0000",
+         "updated_time": "2013-07-26T22:56:04+0000"
+      },
+      {
+         "id": "123_20",
+         "from": {
+            "name": "Person 20",
+            "id": "20"
+         },
+         "message": "Dummy message Person 20",
+         "story": "Person 20 shared photo.",
+         "story_tags": {
+            "0": [
+               {
+                  "id": "20",
+                  "name": "Person 20",
+                  "offset": 0,
+                  "length": 11,
+                  "type": "user"
+               }
+            ],
+            "19": [
+               {
+                  "id": "95",
+                  "name": "Group 95",
+                  "offset": 19,
+                  "length": 27,
+                  "type": "page"
+               }
+            ]
+         },
+         "picture": "https://custom.url/123/20.jpg",
+         "link": "https://link.url/123/20",
+         "name": "Timeline Photos",
+         "caption": "Person 20 link caption",
+         "properties": [
+            {
+               "name": "By",
+               "text": "Group 95",
+               "href": "https://custom.url/123/95"
+            }
+         ],
+         "icon": "https://custom.url/123/20.gif",
+         "privacy": {
+            "value": ""
+         },
+         "type": "photo",
+         "object_id": "456_20",
+         "application": {
+            "name": "Facebook for Android",
+            "namespace": "fbandroid",
+            "id": "456789"
+         },
+         "created_time": "2013-07-26T22:56:04+0000",
+         "updated_time": "2013-07-26T22:56:04+0000"
+      },
+      {
+         "id": "123_21",
+         "from": {
+            "name": "Person 21",
+            "id": "21"
+         },
+         "message": "Dummy message Person 21",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:55:56+0000",
+         "updated_time": "2013-07-26T22:55:56+0000"
+      },
+      {
+         "id": "123_22",
+         "from": {
+            "name": "Person 22",
+            "id": "22"
+         },
+         "message": "Dummy message Person 22",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "948731"
+         },
+         "created_time": "2013-07-26T22:55:38+0000",
+         "updated_time": "2013-07-26T22:55:38+0000"
+      },
+      {
+         "id": "123_23",
+         "from": {
+            "name": "Person 23",
+            "id": "23"
+         },
+         "message": "Dummy message Person 23",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:55:37+0000",
+         "updated_time": "2013-07-26T22:55:37+0000"
+      },
+      {
+         "id": "123_24",
+         "from": {
+            "name": "Person 24",
+            "id": "24"
+         },
+         "message": "Dummy message Person 24",
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "application": {
+            "name": "Mobile",
+            "id": "123456"
+         },
+         "created_time": "2013-07-26T22:55:34+0000",
+         "updated_time": "2013-07-26T22:55:34+0000"
+      },
+      {
+         "id": "123_25",
+         "from": {
+            "category": "Community",
+            "name": "Group 25",
+            "id": "25"
+         },
+         "message": "Dummy message Group 25",
+         "actions": [
+            {
+               "name": "Comment",
+               "link": "https://custom.url/123/25"
+            }
+         ],
+         "privacy": {
+            "value": ""
+         },
+         "type": "status",
+         "created_time": "2013-07-26T22:55:32+0000",
+         "updated_time": "2013-07-26T22:55:32+0000"
+      }
+   ],
+   "paging": {
+      "previous": "https://custom.url/previous",
+      "next": "https://custom.url/next"
+   }
+}
\ No newline at end of file
diff --git a/impl/src/test/resources/jsonmergepatch.json b/impl/src/test/resources/jsonmergepatch.json
new file mode 100644
index 0000000..3b90e5f
--- /dev/null
+++ b/impl/src/test/resources/jsonmergepatch.json
@@ -0,0 +1,103 @@
+[
+        {
+            "target": {},
+            "patch": {"a": {"b":"c"}},
+            "expected": {"a":{"b":"c"}}
+        },
+        {
+            "target": {},
+            "patch": {"a":{"bb":{"ccc":null}}},
+            "expected": {"a":{"bb":{}}}
+        },
+        {
+            "target": {"a":"foo"},
+            "patch": "bar",
+            "expected": "bar"
+        },
+        {
+            "target": {"a":"foo"},
+            "patch": null,
+            "expected": null
+        },
+        {
+            "target": {"a": {"b":"c"}},
+            "patch": {"a": {"b":"d", "c":null}},
+            "expected": {"a": {"b":"d"}}
+        },
+        {
+            "target": { "c": "d" },
+            "patch": { "a": "b" },
+            "expected": { "a": "b", "c": "d" }
+        }, 
+        {
+            "target": { "a": { "d": 2 } },
+            "patch": { "a": { "d": 1 } },
+            "expected": { "a": { "d": 1 } }
+        }, 
+        {
+            "target": { "a": "b", "c": "d" },
+            "patch": { "c": null },
+            "expected": { "a": "b" }
+        }, 
+        {
+            "target": { "a": { "b": "c", "d": null} },
+            "patch": { "a": { "d": null} },
+            "expected": { "a": { "b": "c" } }
+        }, 
+        {
+            "target": {
+              "a": { "b": "c" },
+              "d": "e"
+            },
+            "patch": {
+              "a": 1000010002020389.8787987983
+            },
+            "expected": {
+              "a": 1000010002020389.8787987983,
+              "d": "e"
+            }
+        }, 
+        {
+            "target": { "a": "b" },
+            "patch": { "c": [ null ] },
+            "expected": { "a": "b", "c": [ null ] }
+        }, 
+        {
+            "target": { "a": { "b": null, "d": 3}, "e": -1 },
+            "patch": { "a": { "b": "c", "d": null } },
+            "expected": { "a": { "b": "c" }, "e": -1 }
+        }, 
+        {
+            "target": [1,2],
+            "patch": { "a": "b", "c": null },
+            "expected": { "a": "b"}
+        }, 
+        {
+            "target": {
+              "title": "Goodbye!",
+              "author": {
+                "givenName": "John",
+                "familyName": "Doe"
+              },
+              "tags": [ "example", "sample" ],
+              "content": "This will be unchanged"
+            },
+            "patch": {
+              "title": "Hello!",
+              "phoneNumber": "+01-123-456-7890",
+              "author": {
+                  "familyName": null
+              },
+            "tags": [ "example" ]
+            },
+            "expected": {
+            "title": "Hello!",
+            "author": {
+                "givenName": "John"
+            },
+            "tags": [ "example" ],
+            "content": "This will be unchanged",
+            "phoneNumber": "+01-123-456-7890"
+            }
+        }
+]
\ No newline at end of file
diff --git a/impl/src/test/resources/jsonmergepatchdiff.json b/impl/src/test/resources/jsonmergepatchdiff.json
new file mode 100644
index 0000000..ac6c91d
--- /dev/null
+++ b/impl/src/test/resources/jsonmergepatchdiff.json
@@ -0,0 +1,60 @@
+[
+        {
+            "original": {
+              "title": "Goodbye!",
+              "author": {
+                "givenName": "John",
+                "familyName": "Doe"
+              },
+              "tags": [ "example", "sample" ],
+              "content": "This will be unchanged"
+            },
+            "expected": {
+              "title": "Hello!",
+              "phoneNumber": "+01-123-456-7890",
+              "author": {
+                  "familyName": null
+              },
+            "tags": [ "example" ]
+            },
+            "target": {
+              "title": "Hello!",
+              "author": {
+                  "givenName": "John"
+              },
+              "tags": [ "example" ],
+              "content": "This will be unchanged",
+              "phoneNumber": "+01-123-456-7890"
+            }
+        },
+        {
+            "original": {},
+            "expected": {"a": {"b":"c"}},
+            "target": {"a":{"b":"c"}}
+        },
+        {
+            "original": {"a":"foo"},
+            "expected": "bar",
+            "target": "bar"
+        },
+        {
+            "original": {"a":"foo"},
+            "expected": null,
+            "target": null
+        },
+        {
+            "original": { "c": "d" },
+            "expected": { "a": "b" },
+            "target": { "a": "b", "c": "d" }
+        }, 
+        {
+            "original": { "a": { "d": 2 } },
+            "expected": { "a": { "d": 1 } },
+            "target": { "a": { "d": 1 } }
+        }, 
+        {
+            "original": { "a": "b", "c": "d" },
+            "expected": { "c": null },
+            "target": { "a": "b" }
+        }
+]
\ No newline at end of file
diff --git a/impl/src/test/resources/jsonpatch.json b/impl/src/test/resources/jsonpatch.json
new file mode 100644
index 0000000..4db4794
--- /dev/null
+++ b/impl/src/test/resources/jsonpatch.json
@@ -0,0 +1,219 @@
+[
+        {
+            "op": { "op": "test", "path": "/a/1", "value": "hello" },
+            "target": { "a": [ null, "hello", "world" ] },
+            "expected": { "a": [ null, "hello", "world" ] }
+        },
+        {
+            "op": { "op": "test", "path": "/x", "value": {} },
+            "target": [ 1, 2 ],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "test", "path": "", "value": true },
+            "target": [ 1, 2 ],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "test", "path": "/x", "value": -30.000 },
+            "target": { "x": -29.020 },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "replace", "path": "", "value": false },
+            "target": { "x": { "a": "b", "y": {} } },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "replace", "path": "/x/y", "value": "hello" },
+            "target": { "x": { "a": "b", "y": {} } },
+            "expected": { "x": { "a": "b", "y": "hello" } }
+        },
+        {
+            "op": { "op": "replace", "path": "/0/2", "value": "x" },
+            "target": [ [ "a", "b", "c"], "d", "e" ],
+            "expected": [ [ "a", "b", "x" ], "d", "e" ]
+        },
+        {
+            "op": { "op": "replace", "path": "/x/0", "value": null },
+            "target": { "x": [ "y", "z" ], "foo": "bar" },
+            "expected": { "x": [ null, "z" ], "foo": "bar" }
+        },
+        {
+            "op": { "op": "replace", "path": "/x/y", "value": 42 },
+            "target": { "x": {} },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "remove", "path": "/x/y" },
+            "target": { "x": { "a": "b", "y": {} } },
+            "expected": { "x": { "a": "b" } }
+        },
+        {
+            "op": { "op": "remove", "path": "/0/2" },
+            "target": [ [ "a", "b", "c"], "d", "e" ],
+            "expected": [ [ "a", "b" ], "d", "e" ]
+        },
+        {
+            "op": { "op": "remove", "path": "/x/0" },
+            "target": { "x": [ "y", "z" ], "foo": "bar" },
+            "expected": { "x": [ "z" ], "foo": "bar" }
+        },
+        {
+            "op": { "op": "remove", "path": "/x/y" },
+            "target": { "x": {} },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "move", "from": "/x/a", "path": "/x/b" },
+            "target": { "x": { "a": "helo" } },
+            "expected": { "x": { "b": "helo" } }
+        },
+        {
+            "op": { "op": "move", "from": "/x/a", "path": "/x/a" },
+            "target": { "x": { "a": "helo" } },
+            "expected": { "x": { "a": "helo" } }
+        },
+        {
+            "op": { "op": "move", "from": "/0", "path": "/0/x" },
+            "target": [ "victim", {}, {} ],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "move", "from": "/0", "path": "/-" },
+            "target": [ 0, 1, 2 ],
+            "expected": [ 1, 2, 0 ]
+        },
+        {
+            "op": { "op": "move", "from": "/a", "path": "/b/2" },
+            "target": { "a": "helo", "b": [ 1, 2, 3, 4 ] },
+            "expected": { "b": [ 1, 2, "helo", 3, 4 ] }
+        },
+        {
+            "op": { "op": "move", "from": "/a", "path": "/a/b" },
+            "target": {},
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "move", "from": "/a", "path": "/b/c" },
+            "target": { "a": "b" },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "move", "from": "/x/a", "path": "/x/c" },
+            "target": { "x": { "b": "helo" } },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "copy", "from": "/a", "path": "/b" },
+            "target": { "a": 1 },
+            "expected": { "a": 1, "b": 1 }
+        },
+        {
+            "op": { "op": "copy", "from": "/a", "path": "/b" },
+            "target": { "a": 1, "b": false },
+            "expected": { "a": 1, "b": 1 }
+        },
+        {
+            "op": { "op": "copy", "from": "/0", "path": "/-" },
+            "target": [ 1, 2, 3, 4 ],
+            "expected": [ 1, 2, 3, 4, 1 ]
+        },
+        {
+            "op": { "op": "copy", "from": "/0", "path": "/0" },
+            "target": [ true ],
+            "expected": [ true, true ]
+        },
+        {
+            "op": { "op": "copy", "from": "/a", "path": "/b" },
+            "target": {},
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "copy", "from": "/a", "path": "/b/c" },
+            "target": { "a": 1 },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/a/b/c", "value": 1 },
+            "target": { "a": "b" },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/~1", "value": 1 },
+            "target": [],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/3", "value": 1 },
+            "target": [ 1, 2 ],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/-2", "value": 1 },
+            "target": [ 1, 2 ],
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/foo/f", "value": "bar" },
+            "target": { "foo": "bar" },
+            "exception": "jakarta.json.JsonException"
+        },
+        {
+            "op": { "op": "add", "path": "/a", "value": "b" },
+            "target": {},
+            "expected": { "a": "b" }
+        },
+        {
+            "op": { "op": "add", "path": "/a", "value": 1 },
+            "target": { "a": "b" },
+            "expected": { "a": 1 }
+        },
+        {
+            "op": { "op": "add", "path": "/array/-", "value": 1 },
+            "target": { "array": [ 2, null, {}, 1 ] },
+            "expected": { "array": [ 2, null, {}, 1, 1 ] }
+        },
+        {
+            "op": { "op": "add", "path": "/array/2", "value": "hello" },
+            "target": { "array": [ 2, null, {}, 1] },
+            "expected": { "array": [ 2, null, "hello", {}, 1 ] }
+        },
+        {
+            "op": { "op": "add", "path": "/obj/inner/b", "value": [ 1, 2 ] },
+            "target": {
+                "obj": {
+                    "inner": {
+                        "a": "hello"
+                    }
+                }
+            },
+            "expected": {
+                "obj": {
+                    "inner": {
+                        "a": "hello",
+                        "b": [ 1, 2 ]
+                    }
+                }
+            }
+        },
+        {
+            "op": { "op": "add", "path": "/obj/inner/b", "value": [ 1, 2 ] },
+            "target": {
+                "obj": {
+                    "inner": {
+                        "a": "hello",
+                        "b": "world"
+                    }
+                }
+            },
+            "expected": {
+                "obj": {
+                    "inner": {
+                        "a": "hello",
+                        "b": [ 1, 2 ]
+                    }
+                }
+            }
+        }
+]
diff --git a/impl/src/test/resources/jsonpatchdiff.json b/impl/src/test/resources/jsonpatchdiff.json
new file mode 100644
index 0000000..d4aa8fe
--- /dev/null
+++ b/impl/src/test/resources/jsonpatchdiff.json
@@ -0,0 +1,169 @@
+[
+        {
+            "original": {"a":"b"},
+            "target": {"a":"b"},
+            "expected": []
+        },
+        {
+            "original": [ 1, 2, 3 ],
+            "target": [ 1, 2, 3, 4, 5 ],
+            "expected": [
+                            {"op":"add","path":"/3","value":4},
+                            {"op":"add","path":"/4","value":5}
+                        ]
+        },
+        {
+            "original": [1,2,3,4,5],
+            "target": [1,3,4],
+            "expected": [
+                            { "op": "remove", "path": "/4"},
+                            { "op": "remove", "path": "/1"}
+                        ]
+        },
+        {
+            "original": [1,2,3,4,5,6],
+            "target": [1,7,3,4,8,5],
+            "expected": [
+                            { "op": "remove", "path": "/5"},
+                            { "op": "replace", "path": "/1", "value": 7},
+                            { "op": "add", "path": "/4", "value": 8}
+                        ]
+        },
+        {
+            "original": [ 1, 2, 3 ],
+            "target": [ 1 ],
+            "expected": [
+                            { "op": "remove", "path": "/2" },
+                            { "op": "remove", "path": "/1" }
+                        ]
+        },
+        {
+            "original": { "a": "b", "c": "d" },
+            "target": { "a": "b" },
+            "expected": [
+                            { "op": "remove", "path": "/c" }
+                        ]
+        },
+        {
+            "original": { "a": 1 },
+            "target": { "a": 1, "c": 2, "b": 3, "d": 4 },
+            "expected": [
+                            { "op": "add", "path": "/c", "value": 2 },
+                            { "op": "add", "path": "/b", "value": 3 },
+                            { "op": "add", "path": "/d", "value": 4 }
+                        ]
+        },
+        {
+            "original": { "a": null },
+            "target": { "a": 6 },
+            "expected": [
+                            { "op": "replace", "path": "/a", "value": 6 }
+                        ]
+        },
+        {
+            "original": [ 1, 2, 3 ],
+            "target": { "hello": "world" },
+            "expected": [
+                            { "op": "replace", "path": "", "value": { "hello": "world" } }
+                        ]
+        },
+        {
+            "original": {
+                            "a": "b",
+                            "c": {
+                                    "d": "e"
+                                 }
+                        },
+            "target": {
+                        "a": "b",
+                        "c": {
+                                "d": 1,
+                                "e": "f"
+                             }
+                      },
+            "expected": [
+                            { "op": "replace", "path": "/c/d", "value": 1 },
+                            { "op": "add", "path": "/c/e", "value": "f" }
+                        ]
+        },
+        {
+            "original": {
+                            "a": [ 1, 2, 3 ]
+                        },
+            "target": {
+                        "a": [ "b", 2, 3, 4 ]
+                      },
+            "expected": [
+                            { "op": "replace", "path": "/a/0", "value":"b" },
+                            { "op": "add", "path": "/a/3", "value":4 }
+                        ]
+        },
+        {
+            "original": [ { "a": "b" }, "foo", { "bar": null } ],
+            "target": [ { "a": "b", "c": "d" }, "foo", { "bar": "baz" } ],
+            "expected": [
+                            { "op": "replace", "path": "/2/bar", "value": "baz" },
+                            { "op": "add", "path": "/0/c", "value": "d" }
+                        ]
+        },
+        {
+            "original": [ 1, [ 2, 3 ], 4 ],
+            "target": [ "x", [ 2, 3, "y" ], 4 ],
+            "expected": [
+                            { "op": "add", "path": "/1/2", "value": "y" },
+                            { "op": "replace", "path": "/0", "value": "x" }
+                        ]
+        },
+        {
+            "original": { "a": "b" },
+            "target": { "c": "b" },
+            "expected": [
+                            { "op": "remove", "path": "/a"},
+                            { "op": "add", "path": "/c", "value": "b"}
+                        ]
+        },
+        {
+            "original": {"a": "c"},
+            "target": {"a": "c", "d": "c"},
+            "expected": [
+                            { "op": "add", "path": "/d", "value": "c" }
+                        ]
+        },
+        {
+            "original": [-1, 0, 1, 3, 4],
+            "target": [5, 0],
+            "expected": [
+                            { "path" : "/4", "op" : "remove"},
+                            { "path" : "/3", "op" : "remove"},
+                            { "path" : "/2", "op" : "remove"},
+                            { "value" : 5, "path" : "/0", "op" : "replace" }
+                        ]
+        },
+        {
+            "original": [0],
+            "target": [0, 1, 2, 3, 4],
+            "expected": [
+                            { "path" : "/1", "value" : 1, "op" : "add" },
+                            { "path" : "/2", "value" : 2, "op" : "add" },
+                            { "value" : 3, "path" : "/3", "op" : "add" },
+                            { "op" : "add", "path" : "/4", "value" : 4 }
+                        ]
+        },
+        {
+            "original": [0, 2, 4],
+            "target": [0, 1, 2, 3, 4],
+            "expected": [
+                            { "path" : "/1", "value" : 1, "op" : "add" },
+                            { "value" : 3, "op" : "add", "path" : "/3" }
+                        ]
+        },
+        {
+          "original": {"a/b": "c", "e/f":  "i"},
+          "target": {"a/b": "d", "f/g":  "i"},
+          "expected": [
+            { "op": "replace", "path": "/a~1b", "value": "d" },
+            { "op": "remove", "path":"/e~1f" },
+            { "op": "add", "path":"/f~1g", "value":"i" }
+          ]
+        }
+]
diff --git a/impl/src/test/resources/rfc6901.json b/impl/src/test/resources/rfc6901.json
new file mode 100644
index 0000000..170147b
--- /dev/null
+++ b/impl/src/test/resources/rfc6901.json
@@ -0,0 +1,20 @@
+   {
+      "foo": ["bar", "baz"],
+      "": 0,
+      "a/b": 1,
+      "c%d": 2,
+      "e^f": 3,
+      "g|h": 4,
+      "i\\j": 5,
+      "k\"l": 6,
+      " ": 7,
+      "m~n": 8,
+      "o" : null,
+      "p": {
+        "q":"r"
+       },
+       "s": [ {
+                "t":"u"
+              }
+       ]
+   }
\ No newline at end of file
diff --git a/impl/src/test/resources/twitter.json b/impl/src/test/resources/twitter.json
new file mode 100644
index 0000000..677f2b9
--- /dev/null
+++ b/impl/src/test/resources/twitter.json
@@ -0,0 +1,2 @@
+{"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"de"},"created_at":"Fri Jul 26 19:31:26 +0000 2013","id":360844831439855616,"id_str":"360844831439855616","text":"Hiring ••► Pre-Sales Consultant ☛ http://t.co/fjlOd8PhcQ #Java #JUnit #Python #PHP #XML #XSLT #SOAP #REST #SAML #Node","source":"<a href=\"http://www.socialoomph.com\" rel=\"nofollow\">SocialOomph</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":93646439,"id_str":"93646439","name":"Paul Pleus","screen_name":"IER_Recruiting","location":"Worldwide","description":"International Executive Recruiting – IER, Xing, Linkedin, Recruiting, Recruiter, Headhunter Personalberater, Job, Jobs, Stellenangebot, Manager","url":"http://t.co/GtcxFsUyI3","entities":{"url":{"urls":[{"url":"http://t.co/GtcxFsUyI3","expanded_url":"http://www.ier-network.com","display_url":"ier-network.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":17822,"friends_count":15829,"listed_count":119,"created_at":"Mon Nov 30 15:02:59 +0000 2009","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":26733,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"0099B9","profile_background_image_url":"http://a0.twimg.com/profile_background_images/216930247/pleus_final_logo_KUGEL.jpg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/216930247/pleus_final_logo_KUGEL.jpg","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1270812893/Final_198x146_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/1270812893/Final_198x146_normal.jpg","profile_link_color":"0099B9","profile_sidebar_border_color":"5ED4DC","profile_sidebar_fill_color":"95E8EC","profile_text_color":"3C3940","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[57,62]},{"text":"JUnit","indices":[63,69]},{"text":"Python","indices":[70,77]},{"text":"PHP","indices":[78,82]},{"text":"XML","indices":[83,87]},{"text":"XSLT","indices":[88,93]},{"text":"SOAP","indices":[94,99]},{"text":"REST","indices":[100,105]},{"text":"SAML","indices":[106,111]},{"text":"Node","indices":[112,117]}],"symbols":[],"urls":[{"url":"http://t.co/fjlOd8PhcQ","expanded_url":"http://bit.ly/19I4P7E","display_url":"bit.ly/19I4P7E","indices":[34,56]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"de"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:39 +0000 2013","id":360844632520798209,"id_str":"360844632520798209","text":"Death toll reaches 15 for #Indonesian asylum seeker boat that capsized off the coast of west #Java http://t.co/4rGk0LO9F8","source":"<a href=\"http://www.tweetdeck.com\" rel=\"nofollow\">TweetDeck</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":90407341,"id_str":"90407341","name":"CSIS Southeast Asia","screen_name":"SoutheastAsiaDC","location":"Washington, DC","description":"The Sumitro Chair for Southeast Asia Studies @CSIS is the premier forum for sustained elevated policy dialogue on Southeast Asia and US interests in the region.","url":"http://t.co/1bcQU6sG1V","entities":{"url":{"urls":[{"url":"http://t.co/1bcQU6sG1V","expanded_url":"http://bit.ly/csis-seap","display_url":"bit.ly/csis-seap","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":8127,"friends_count":523,"listed_count":427,"created_at":"Mon Nov 16 14:51:00 +0000 2009","favourites_count":16,"utc_offset":-14400,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":12019,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http://a0.twimg.com/profile_background_images/669153955/be1298700d512cfdd3734a677a587095.gif","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/669153955/be1298700d512cfdd3734a677a587095.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2648645038/e06eb649efb4030845d4cbf47d41651b_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2648645038/e06eb649efb4030845d4cbf47d41651b_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/90407341/1371826204","profile_link_color":"93A644","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Indonesian","indices":[26,37]},{"text":"Java","indices":[93,98]}],"symbols":[],"urls":[{"url":"http://t.co/4rGk0LO9F8","expanded_url":"http://fxn.ws/1c7VZ2F","display_url":"fxn.ws/1c7VZ2F","indices":[99,121]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:15 +0000 2013","id":360844531593261057,"id_str":"360844531593261057","text":"Senior Java Developer w/ #Java #Software skills Addison @p2people http://t.co/Fc9UlSdyWj","source":"<a href=\"http://www.p2people.co.uk\" rel=\"nofollow\">p2people</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":551581457,"id_str":"551581457","name":"p2p WebMobileIT","screen_name":"p2pWebMobileIt","location":"Stevenage | Hertfordshire","description":"Web, Mobile & IT micro #outsourcing and #freelance jobs at p2people","url":"http://t.co/OxLhyJdlxw","entities":{"url":{"urls":[{"url":"http://t.co/OxLhyJdlxw","expanded_url":"http://www.p2people.co.uk","display_url":"p2people.co.uk","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":712,"friends_count":3,"listed_count":44,"created_at":"Thu Apr 12 05:07:17 +0000 2012","favourites_count":0,"utc_offset":3600,"time_zone":"London","geo_enabled":false,"verified":false,"statuses_count":88202,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2099631495/p2people_logo_48x48_normal.png","profile_image_url_https":"https://si0.twimg.com/profile_images/2099631495/p2people_logo_48x48_normal.png","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[25,30]},{"text":"Software","indices":[31,40]}],"symbols":[],"urls":[{"url":"http://t.co/Fc9UlSdyWj","expanded_url":"http://bit.ly/175ch6w","display_url":"bit.ly/175ch6w","indices":[66,88]}],"user_mentions":[{"screen_name":"p2people","name":"p2people","id":22741835,"id_str":"22741835","indices":[56,65]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:00 +0000 2013","id":360844471761502209,"id_str":"360844471761502209","text":"Took 658ms to processing 200 tweets #JAVA #APPENGINE #1374867000804","source":"<a href=\"http://google.co.id/\" rel=\"nofollow\">You Can't Be Wrong</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1186398894,"id_str":"1186398894","name":"You Can't Be Wrong","screen_name":"YouCantBeWrong","location":"@tegaralaga's mind","description":"User generated content text game based on Twitter. For more information send your email to tegaralaga(at)live(dot)com","url":"http://t.co/Jto68Xkf","entities":{"url":{"urls":[{"url":"http://t.co/Jto68Xkf","expanded_url":"http://google.co.id/","display_url":"google.co.id","indices":[0,20]}]},"description":{"urls":[]}},"protected":false,"followers_count":8,"friends_count":1,"listed_count":1,"created_at":"Sat Feb 16 14:30:21 +0000 2013","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":22815,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1186398894/1361117710","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"JAVA","indices":[36,41]},{"text":"APPENGINE","indices":[42,52]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:29:22 +0000 2013","id":360844310297591811,"id_str":"360844310297591811","text":"RT @ECBader: Some interesting open source #Java projects from #Esri http://t.co/1pvAEzkV9r. Geometry API is worth checking out! https://t.c…","source":"<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":11640022,"id_str":"11640022","name":"Philip Heede","screen_name":"pheede","location":"Redlands, CA","description":"Transplanted Dane using GIS at Esri w/.NET/XAML/Geoprocessing/Python/ArcObjects/Network Analyst/.. Disclaimer: all opinions, comments are my own, blah blah..","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":355,"friends_count":267,"listed_count":22,"created_at":"Sat Dec 29 20:25:10 +0000 2007","favourites_count":114,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":1711,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/335064929/n701240139_383204_9480_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/335064929/n701240139_383204_9480_normal.jpg","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 15:40:13 +0000 2013","id":360786645261352961,"id_str":"360786645261352961","text":"Some interesting open source #Java projects from #Esri http://t.co/1pvAEzkV9r. Geometry API is worth checking out! https://t.co/yEXKNTClKZ","source":"<a href=\"http://www.tweetdeck.com\" rel=\"nofollow\">TweetDeck</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":26422164,"id_str":"26422164","name":"Eric Bader","screen_name":"ECBader","location":"Southern California","description":"Geographer, Musician, Christ-follower, Java dude, Nebraska Cornhusker, Esri Product Management Team - ArcGIS.","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":614,"friends_count":473,"listed_count":37,"created_at":"Wed Mar 25 03:44:15 +0000 2009","favourites_count":27,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":2700,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2586630286/dct7znna7xosntblt7jb_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2586630286/dct7znna7xosntblt7jb_normal.jpeg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":2,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[29,34]},{"text":"Esri","indices":[49,54]}],"symbols":[],"urls":[{"url":"http://t.co/1pvAEzkV9r","expanded_url":"http://esri.github.io/#Java","display_url":"esri.github.io/#Java","indices":[55,77]},{"url":"https://t.co/yEXKNTClKZ","expanded_url":"https://github.com/Esri/geometry-api-java","display_url":"github.com/Esri/geometry-…","indices":[115,138]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"retweet_count":2,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[42,47]},{"text":"Esri","indices":[62,67]}],"symbols":[],"urls":[{"url":"http://t.co/1pvAEzkV9r","expanded_url":"http://esri.github.io/#Java","display_url":"esri.github.io/#Java","indices":[68,90]}],"user_mentions":[{"screen_name":"ECBader","name":"Eric Bader","id":26422164,"id_str":"26422164","indices":[3,11]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:29:17 +0000 2013","id":360844289841971200,"id_str":"360844289841971200","text":"Looking for a new IT challenge? Take a look at our job openings http://t.co/MhnnR101cT #atlassian #java #oracle #mobile #infra #jobs","source":"<a href=\"http://www.tweetdeck.com\" rel=\"nofollow\">TweetDeck</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":296655269,"id_str":"296655269","name":"Koen Gillard","screen_name":"KoenGillard","location":"","description":"Sr Java/Oracle Consultant at @Contribute4J. Certified Scrum Master. Big Atlassian enthusiast. Father of 2 great sons. Other interests music/soccer/cycling/...","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":151,"friends_count":341,"listed_count":4,"created_at":"Wed May 11 05:23:43 +0000 2011","favourites_count":874,"utc_offset":7200,"time_zone":"Brussels","geo_enabled":false,"verified":false,"statuses_count":549,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"ACDED6","profile_background_image_url":"http://a0.twimg.com/images/themes/theme18/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme18/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3690118960/3bf92c8b7f1398f35e635f0d08bde368_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3690118960/3bf92c8b7f1398f35e635f0d08bde368_normal.jpeg","profile_link_color":"038543","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"F6F6F6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"atlassian","indices":[87,97]},{"text":"java","indices":[98,103]},{"text":"oracle","indices":[104,111]},{"text":"mobile","indices":[112,119]},{"text":"infra","indices":[120,126]},{"text":"jobs","indices":[127,132]}],"symbols":[],"urls":[{"url":"http://t.co/MhnnR101cT","expanded_url":"http://jobs.contribute.be","display_url":"jobs.contribute.be","indices":[64,86]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:27:56 +0000 2013","id":360843950518566914,"id_str":"360843950518566914","text":"RT @SKSMediaMalay: Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"<a href=\"http://roundteam.co\" rel=\"nofollow\">RoundTeam</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1596343046,"id_str":"1596343046","name":"Rooms_In_Lowestoft","screen_name":"RoomsLowestoft","location":"Lowestoft, Suffolk, UK","description":"#Rooms #Room to #rent #let in #Lowestoft, #Suffolk, #UK E: lowestoft@niche.me.uk \r\n\r\n#norfolk #greatyarmouth #saxmundham #ipswich #aldeburgh #leiston #tfbuk","url":"https://t.co/aeCwVfy3l0","entities":{"url":{"urls":[{"url":"https://t.co/aeCwVfy3l0","expanded_url":"https://twitter.com/RoomsLowestoft","display_url":"twitter.com/RoomsLowestoft","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":963,"friends_count":1742,"listed_count":14,"created_at":"Mon Jul 15 17:23:58 +0000 2013","favourites_count":182,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":9095,"lang":"en-gb","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/378800000024425110/a2acf9f2d9bf8f8c90cb24910a71c037.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/378800000024425110/a2acf9f2d9bf8f8c90cb24910a71c037.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/378800000138549193/b276cf4d94b9c96e5b61c92f9864615f_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000138549193/b276cf4d94b9c96e5b61c92f9864615f_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1596343046/1373917379","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:20:50 +0000 2013","id":360842165292773376,"id_str":"360842165292773376","text":"Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"<a href=\"http://www.hootsuite.com\" rel=\"nofollow\">HootSuite</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1449573356,"id_str":"1449573356","name":"SKS Media Malaysia","screen_name":"SKSMediaMalay","location":"Kuala Lumpur","description":"Pemasaran syarikat perkhidmatan.\r\n\r\n#teamfollowback #tfb #tfbjp #autofollow #followback","url":"https://t.co/nfmdo6g6HC","entities":{"url":{"urls":[{"url":"https://t.co/nfmdo6g6HC","expanded_url":"https://twitter.com/SKSMediaMalay","display_url":"twitter.com/SKSMediaMalay","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":1048,"friends_count":877,"listed_count":27,"created_at":"Wed May 22 18:07:31 +0000 2013","favourites_count":166,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":8006,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[84,91]},{"text":"Lampung","indices":[92,100]},{"text":"java","indices":[101,106]},{"text":"Sumatra","indices":[107,115]},{"text":"Banten","indices":[116,123]},{"text":"Riau","indices":[124,129]},{"text":"Padang","indices":[130,137]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[61,83]}],"user_mentions":[{"screen_name":"SKSMediaMalay","name":"SKS Media Malaysia","id":1449573356,"id_str":"1449573356","indices":[3,17]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:25:27 +0000 2013","id":360843324011184128,"id_str":"360843324011184128","text":"#Java #Developer #BackEnd position open in #Dallas TX. #BED #BE #JavaDev Apply Online Today! - http://t.co/oVDyhoR65M","source":"<a href=\"http://www.hootsuite.com\" rel=\"nofollow\">HootSuite</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":52097984,"id_str":"52097984","name":"InSource Group","screen_name":"InSourcegroup","location":"Dallas, Texas","description":"Technical Resource & Contract Staffing Provider with offices in Dallas, Fort Worth, and Houston.","url":"http://t.co/IyUTPyeCMm","entities":{"url":{"urls":[{"url":"http://t.co/IyUTPyeCMm","expanded_url":"http://www.insourcegroup.com","display_url":"insourcegroup.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":1712,"friends_count":1899,"listed_count":63,"created_at":"Mon Jun 29 16:03:36 +0000 2009","favourites_count":81,"utc_offset":-18000,"time_zone":"Central Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":43289,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"FFFFFF","profile_background_image_url":"http://a0.twimg.com/profile_background_images/57804915/nvoffice.br.jpg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/57804915/nvoffice.br.jpg","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1109935707/insource-fb_normal.gif","profile_image_url_https":"https://si0.twimg.com/profile_images/1109935707/insource-fb_normal.gif","profile_banner_url":"https://pbs.twimg.com/profile_banners/52097984/1369854759","profile_link_color":"0083FF","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"7ABAE6","profile_text_color":"000000","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[0,5]},{"text":"Developer","indices":[6,16]},{"text":"BackEnd","indices":[17,25]},{"text":"Dallas","indices":[43,50]},{"text":"BED","indices":[55,59]},{"text":"BE","indices":[60,63]},{"text":"JavaDev","indices":[64,72]}],"symbols":[],"urls":[{"url":"http://t.co/oVDyhoR65M","expanded_url":"http://ow.ly/nkcSS","display_url":"ow.ly/nkcSS","indices":[95,117]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:22:18 +0000 2013","id":360842533410045954,"id_str":"360842533410045954","text":"3,2,1... Stir and energy will be served! #coffee #addict #givemecaffine #java http://t.co/OjGso3pbPt","source":"<a href=\"http://instagram.com\" rel=\"nofollow\">Instagram</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":17855557,"id_str":"17855557","name":"Heather Cereghino","screen_name":"HeatherCinOC","location":"Orange County, CA","description":"Social media-lite, creative writing (beach + laptop = bliss), Event Planner, PR, cook in the making, love the outdoors, traveling & shoes. PS. I love shoes!","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":4114,"friends_count":4410,"listed_count":232,"created_at":"Thu Dec 04 01:19:51 +0000 2008","favourites_count":218,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":12505,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http://a0.twimg.com/images/themes/theme13/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme13/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3673021821/693873aafc52e32076ff85d68302ff78_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3673021821/693873aafc52e32076ff85d68302ff78_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/17855557/1370094052","profile_link_color":"93A644","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"coffee","indices":[41,48]},{"text":"addict","indices":[49,56]},{"text":"givemecaffine","indices":[57,71]},{"text":"java","indices":[72,77]}],"symbols":[],"urls":[{"url":"http://t.co/OjGso3pbPt","expanded_url":"http://instagram.com/p/cPZ9yEqwlf/","display_url":"instagram.com/p/cPZ9yEqwlf/","indices":[78,100]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:20:50 +0000 2013","id":360842165292773376,"id_str":"360842165292773376","text":"Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"<a href=\"http://www.hootsuite.com\" rel=\"nofollow\">HootSuite</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1449573356,"id_str":"1449573356","name":"SKS Media Malaysia","screen_name":"SKSMediaMalay","location":"Kuala Lumpur","description":"Pemasaran syarikat perkhidmatan.\r\n\r\n#teamfollowback #tfb #tfbjp #autofollow #followback","url":"https://t.co/nfmdo6g6HC","entities":{"url":{"urls":[{"url":"https://t.co/nfmdo6g6HC","expanded_url":"https://twitter.com/SKSMediaMalay","display_url":"twitter.com/SKSMediaMalay","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":1048,"friends_count":877,"listed_count":27,"created_at":"Wed May 22 18:07:31 +0000 2013","favourites_count":166,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":8006,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:20:08 +0000 2013","id":360841988431556609,"id_str":"360841988431556609","text":"Took 8397ms to processing 200 tweets #JAVA #APPENGINE #1374866408704","source":"<a href=\"http://google.co.id/\" rel=\"nofollow\">You Can't Be Wrong</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1186398894,"id_str":"1186398894","name":"You Can't Be Wrong","screen_name":"YouCantBeWrong","location":"@tegaralaga's mind","description":"User generated content text game based on Twitter. For more information send your email to tegaralaga(at)live(dot)com","url":"http://t.co/Jto68Xkf","entities":{"url":{"urls":[{"url":"http://t.co/Jto68Xkf","expanded_url":"http://google.co.id/","display_url":"google.co.id","indices":[0,20]}]},"description":{"urls":[]}},"protected":false,"followers_count":8,"friends_count":1,"listed_count":1,"created_at":"Sat Feb 16 14:30:21 +0000 2013","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":22815,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1186398894/1361117710","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"JAVA","indices":[37,42]},{"text":"APPENGINE","indices":[43,53]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:18:09 +0000 2013","id":360841487262547968,"id_str":"360841487262547968","text":"Can’t decide between more #java or #c++","source":"<a href=\"http://tapbots.com/tweetbot\" rel=\"nofollow\">Tweetbot for iOS</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":29063267,"id_str":"29063267","name":"Steven Truesdell","screen_name":"TrueSteve","location":"Denver, CO","description":"Snowboarder. Photographer. Web Designer. Journalist. Music lover. Soon to be Firefighter & EMT","url":"http://t.co/fCPFLstB49","entities":{"url":{"urls":[{"url":"http://t.co/fCPFLstB49","expanded_url":"http://www.steventruesdell.com","display_url":"steventruesdell.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":88,"friends_count":144,"listed_count":3,"created_at":"Sun Apr 05 21:02:17 +0000 2009","favourites_count":0,"utc_offset":-21600,"time_zone":"Mountain Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":1792,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3637401835/e96f6955cd6bfd3d7bf78002ebdbd0de_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3637401835/e96f6955cd6bfd3d7bf78002ebdbd0de_normal.jpeg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":{"id":"2571b7720cd62ad3","url":"https://api.twitter.com/1.1/geo/id/2571b7720cd62ad3.json","place_type":"city","name":"Highlands Ranch","full_name":"Highlands Ranch, CO","country_code":"US","country":"United States","bounding_box":{"type":"Polygon","coordinates":[[[-105.05306,39.510546],[-104.899484,39.510546],[-104.899484,39.566782],[-105.05306,39.566782]]]},"attributes":{}},"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"java","indices":[26,31]},{"text":"c","indices":[35,37]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"pt"},"created_at":"Fri Jul 26 19:17:48 +0000 2013","id":360841400004251648,"id_str":"360841400004251648","text":"RT @maribalbe: No Brasil? Piada! RT @rogerio_gentil: Desenvolvedores em #PHP e #Java ganham até R$ 9 mil http://t.co/Cj6dotO8Be // #souDev","source":"<a href=\"http://www.tweetdeck.com\" rel=\"nofollow\">TweetDeck</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":188855964,"id_str":"188855964","name":"Rodrigo Baron","screen_name":"b4r0n__","location":"Medianeira","description":"Full-stack Rails Developer. Addicts script codes and Internet App Dev. Startup Co-Founder: http://t.co/Vvj2Un84yC","url":"http://t.co/WEJSgUafv6","entities":{"url":{"urls":[{"url":"http://t.co/WEJSgUafv6","expanded_url":"http://about.me/baron.rodrigo","display_url":"about.me/baron.rodrigo","indices":[0,22]}]},"description":{"urls":[{"url":"http://t.co/Vvj2Un84yC","expanded_url":"http://www.saifer.com.br","display_url":"saifer.com.br","indices":[91,113]}]}},"protected":false,"followers_count":121,"friends_count":183,"listed_count":1,"created_at":"Thu Sep 09 19:15:20 +0000 2010","favourites_count":105,"utc_offset":-10800,"time_zone":"Brasilia","geo_enabled":true,"verified":false,"statuses_count":3777,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"FFFFFF","profile_background_image_url":"http://a0.twimg.com/images/themes/theme14/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme14/bg.gif","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/378800000105833766/cfe4372a0fce180e09813ab02f909a4d_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000105833766/cfe4372a0fce180e09813ab02f909a4d_normal.jpeg","profile_link_color":"006DCC","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":false,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"pt"},"created_at":"Fri Jul 26 14:37:46 +0000 2013","id":360770928357027840,"id_str":"360770928357027840","text":"No Brasil? Piada! RT @rogerio_gentil: Desenvolvedores em #PHP e #Java ganham até R$ 9 mil http://t.co/Cj6dotO8Be // #souDev","source":"<a href=\"http://www.tweetdeck.com\" rel=\"nofollow\">TweetDeck</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":41602902,"id_str":"41602902","name":"Marília Balbé","screen_name":"maribalbe","location":"Florianópolis, Brazil","description":"Certified Scrum Master. MCTS Microsoft Project 2010, Managing Projects. MTAC. Gaúcha. Feeling. Liderança. Agilidade. Follow me!","url":"http://t.co/v7BRmRMPEn","entities":{"url":{"urls":[{"url":"http://t.co/v7BRmRMPEn","expanded_url":"http://mariliabalbe.com","display_url":"mariliabalbe.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":2065,"friends_count":1464,"listed_count":81,"created_at":"Thu May 21 15:05:11 +0000 2009","favourites_count":51,"utc_offset":-10800,"time_zone":"Brasilia","geo_enabled":true,"verified":false,"statuses_count":26222,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"9AE4E8","profile_background_image_url":"http://a0.twimg.com/images/themes/theme16/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme16/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2914714002/d2b0d4771ad8c92841a8d6a37d24c920_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2914714002/d2b0d4771ad8c92841a8d6a37d24c920_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"BDDCAD","profile_sidebar_fill_color":"DDFFCC","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":21,"favorite_count":1,"entities":{"hashtags":[{"text":"PHP","indices":[57,61]},{"text":"Java","indices":[64,69]},{"text":"souDev","indices":[116,123]}],"symbols":[],"urls":[{"url":"http://t.co/Cj6dotO8Be","expanded_url":"http://lnkd.in/zsQhdg","display_url":"lnkd.in/zsQhdg","indices":[90,112]}],"user_mentions":[{"screen_name":"rogerio_gentil","name":"Rogerio J. Gentil","id":71697548,"id_str":"71697548","indices":[21,36]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"pt"},"retweet_count":21,"favorite_count":0,"entities":{"hashtags":[{"text":"PHP","indices":[72,76]},{"text":"Java","indices":[79,84]},{"text":"souDev","indices":[131,138]}],"symbols":[],"urls":[{"url":"http://t.co/Cj6dotO8Be","expanded_url":"http://lnkd.in/zsQhdg","display_url":"lnkd.in/zsQhdg","indices":[105,127]}],"user_mentions":[{"screen_name":"maribalbe","name":"Marília Balbé","id":41602902,"id_str":"41602902","indices":[3,13]},{"screen_name":"rogerio_gentil","name":"Rogerio J. Gentil","id":71697548,"id_str":"71697548","indices":[36,51]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"pt"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:16:15 +0000 2013","id":360841008411447296,"id_str":"360841008411447296","text":"RT @danielleandy: Finding a way to have our morning coffee together! #bestneighbors #technology  #java #screenshot… http://t.co/ZakQ5Ip16C","source":"<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":169605040,"id_str":"169605040","name":"Bucko","screen_name":"jonathonbuckley","location":"Los Angeles","description":"","url":"http://t.co/Vsgepye3eK","entities":{"url":{"urls":[{"url":"http://t.co/Vsgepye3eK","expanded_url":"http://jonathonbuckley.com","display_url":"jonathonbuckley.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":2059,"friends_count":116,"listed_count":86,"created_at":"Thu Jul 22 18:56:12 +0000 2010","favourites_count":5,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":436,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1097180716/8X10_6255_no_pink_small_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/1097180716/8X10_6255_no_pink_small_normal.jpg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 16:44:25 +0000 2013","id":360802801032499200,"id_str":"360802801032499200","text":"Finding a way to have our morning coffee together! #bestneighbors #technology  #java #screenshot… http://t.co/ZakQ5Ip16C","source":"<a href=\"http://instagram.com\" rel=\"nofollow\">Instagram</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":259458041,"id_str":"259458041","name":"Danielle Anderson","screen_name":"danielleandy","location":"Los Angeles ","description":"","url":"http://t.co/JiAcLT2Wfg","entities":{"url":{"urls":[{"url":"http://t.co/JiAcLT2Wfg","expanded_url":"http://www.danielleandy.com","display_url":"danielleandy.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":209,"friends_count":127,"listed_count":3,"created_at":"Tue Mar 01 23:31:29 +0000 2011","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":1451,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/378800000130553531/82c57daecf779a8d61f17a53a44d31d3_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000130553531/82c57daecf779a8d61f17a53a44d31d3_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/259458041/1373653784","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"bestneighbors","indices":[51,65]},{"text":"technology","indices":[66,77]},{"text":"java","indices":[79,84]},{"text":"screenshot","indices":[85,96]}],"symbols":[],"urls":[{"url":"http://t.co/ZakQ5Ip16C","expanded_url":"http://instagram.com/p/cPHow7KIcS/","display_url":"instagram.com/p/cPHow7KIcS/","indices":[98,120]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"bestneighbors","indices":[69,83]},{"text":"technology","indices":[84,95]},{"text":"java","indices":[97,102]},{"text":"screenshot","indices":[103,114]}],"symbols":[],"urls":[{"url":"http://t.co/ZakQ5Ip16C","expanded_url":"http://instagram.com/p/cPHow7KIcS/","display_url":"instagram.com/p/cPHow7KIcS/","indices":[116,138]}],"user_mentions":[{"screen_name":"danielleandy","name":"Danielle Anderson","id":259458041,"id_str":"259458041","indices":[3,16]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:11:01 +0000 2013","id":360839690812784641,"id_str":"360839690812784641","text":"Rip-off or investor's dream? You decide...http://t.co/yLzRPs1Qly #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"<a href=\"http://www.hootsuite.com\" rel=\"nofollow\">HootSuite</a>","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":854562582,"id_str":"854562582","name":"SKS Media Londres","screen_name":"SKSMediaLondres","location":"","description":"Nous sommes aussi #teamfollowback #followback @sksfollowback","url":"http://t.co/g6dzOOabDQ","entities":{"url":{"urls":[{"url":"http://t.co/g6dzOOabDQ","expanded_url":"http://www.niche-advertising.co.uk","display_url":"niche-advertising.co.uk","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":1972,"friends_count":1380,"listed_count":100,"created_at":"Sun Sep 30 11:39:09 +0000 2012","favourites_count":439,"utc_offset":7200,"time_zone":"Amsterdam","geo_enabled":false,"verified":false,"statuses_count":144790,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/673161806/9c3894fdf100336e2f53e97bffeace6c.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/673161806/9c3894fdf100336e2f53e97bffeace6c.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/2665504502/86c0fc8adfb2d7f13e687f7dbfddf876_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2665504502/86c0fc8adfb2d7f13e687f7dbfddf876_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/yLzRPs1Qly","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"}],"search_metadata":{"completed_in":0.033,"max_id":360844831439855616,"max_id_str":"360844831439855616","next_results":"?max_id=360839690812784640&q=%23java&include_entities=1","query":"%23java","refresh_url":"?since_id=360844831439855616&q=%23java&include_entities=1","count":15,"since_id":0,"since_id_str":"0"}}
+
diff --git a/impl/src/test/resources/wiki.json b/impl/src/test/resources/wiki.json
new file mode 100644
index 0000000..17bb193
--- /dev/null
+++ b/impl/src/test/resources/wiki.json
@@ -0,0 +1,21 @@
+{
+     "firstName": "John",
+     "lastName": "Smith",
+     "age": 25,
+     "address": {
+         "streetAddress": "21 2nd Street",
+         "city": "New York",
+         "state": "NY",
+         "postalCode": "10021"
+     },
+     "phoneNumber": [
+         {
+           "type": "home",
+           "number": "212 555-1234"
+         },
+         {
+           "type": "fax",
+           "number": "646 555-4567"
+         }
+     ]
+}
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
new file mode 100644
index 0000000..1f2222b
--- /dev/null
+++ b/jaxrs/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish</groupId>
+        <artifactId>json</artifactId>
+        <version>2.0.1</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>jsonp-jaxrs</artifactId>
+    <packaging>jar</packaging>
+    <version>2.0.1</version>
+    <name>Jakarta JSON Processing Media for Jakarta RESTful Web Services</name>
+    <description>Jakarta RESTful Web Services MessageBodyReader and MessageBodyWriter to support JsonValue API of Jakarta JSON Processing</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.ws.rs</groupId>
+            <artifactId>jakarta.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/jaxrs/src/main/java/module-info.java b/jaxrs/src/main/java/module-info.java
new file mode 100644
index 0000000..79fbbc8
--- /dev/null
+++ b/jaxrs/src/main/java/module-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+module org.glassfish.json.jaxrs {
+
+    requires jakarta.json;
+    requires jakarta.annotation;
+    requires jakarta.ws.rs;
+
+    exports org.glassfish.json.jaxrs;
+
+}
diff --git a/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java
new file mode 100644
index 0000000..939982d
--- /dev/null
+++ b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.jaxrs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import jakarta.json.Json;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.JsonValue;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.Provider;
+
+/**
+ * Jakarta RESTful Web Services MessageBodyReader for JsonValue.
+ * This allows JsonValue to be a parameter of a resource method.
+ *
+ * @author Jitendra Kotamraju
+ * @author Blaise Doughan
+ * @author Michal Gajdos
+ */
+@Provider
+@Consumes({"application/json", "text/json", "*/*"})
+public class JsonValueBodyReader implements MessageBodyReader<JsonValue> {
+    private final JsonReaderFactory rf = Json.createReaderFactory(null);
+
+    private static final String JSON = "json";
+    private static final String PLUS_JSON = "+json";
+
+    @Override
+    public boolean isReadable(Class<?> aClass, Type type,
+            Annotation[] annotations, MediaType mediaType) {
+        return JsonValue.class.isAssignableFrom(aClass) && supportsMediaType(mediaType);
+    }
+
+    /**
+     * @return true for all media types of the pattern *&#47;json and
+     * *&#47;*+json.
+     */
+    private static boolean supportsMediaType(final MediaType mediaType) {
+        return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON);
+    }
+
+    @Override
+    public JsonValue readFrom(Class<JsonValue> jsonValueClass,
+            Type type, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> stringStringMultivaluedMap,
+            InputStream inputStream) throws IOException, WebApplicationException {
+        try (JsonReader reader = rf.createReader(inputStream)) {
+            return reader.readValue();
+        }
+    }
+}
diff --git a/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java
new file mode 100644
index 0000000..35f2729
--- /dev/null
+++ b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.json.jaxrs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import jakarta.annotation.PostConstruct;
+import jakarta.json.Json;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonWriter;
+import jakarta.json.JsonWriterFactory;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import jakarta.ws.rs.ext.Provider;
+
+/**
+ * Jakarta RESTful Web Services MessageBodyWriter for JsonValue.
+ * This allows JsonValue to be return type of a resource method.
+ *
+ * @author Jitendra Kotamraju
+ * @author Blaise Doughan
+ * @author Michal Gajdos
+ */
+@Provider
+@Produces({"application/json", "text/json", "*/*"})
+public class JsonValueBodyWriter implements MessageBodyWriter<JsonValue> {
+    private static final String JSON = "json";
+    private static final String PLUS_JSON = "+json";
+
+    private JsonWriterFactory wf = Json.createWriterFactory(null);
+
+    @Context
+    private Configuration config;
+
+    @PostConstruct
+    private void init() {
+        Map<String, Object> props = new HashMap<>();
+        if (config != null && config.getProperties().containsKey(JsonGenerator.PRETTY_PRINTING)) {
+            props.put(JsonGenerator.PRETTY_PRINTING, true);
+        }
+        wf = Json.createWriterFactory(props);
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> aClass,
+            Type type, Annotation[] annotations, MediaType mediaType) {
+        return JsonValue.class.isAssignableFrom(aClass) && supportsMediaType(mediaType);
+    }
+
+    /**
+     * @return true for all media types of the pattern *&#47;json and
+     * *&#47;*+json.
+     */
+    private static boolean supportsMediaType(final MediaType mediaType) {
+        return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON);
+    }
+
+    @Override
+    public long getSize(JsonValue jsonValue, Class<?> aClass,
+            Type type, Annotation[] annotations, MediaType mediaType) {
+
+        return -1;
+    }
+
+    @Override
+    public void writeTo(JsonValue jsonValue, Class<?> aClass, Type type,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> stringObjectMultivaluedMap,
+            OutputStream outputStream) throws IOException, WebApplicationException {
+        try (JsonWriter writer = wf.createWriter(outputStream)) {
+            writer.write(jsonValue);
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f22b709
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,464 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.6</version>
+    </parent>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>json</artifactId>
+    <packaging>pom</packaging>
+    <version>2.0.1</version>
+    <name>Jakarta JSON Processing</name>
+    <description>Jakarta JSON Processing defines a Java(R) based framework for parsing, generating, transforming, and querying JSON documents.</description>
+    <url>https://github.com/eclipse-ee4j/jsonp</url>
+
+    <scm>
+        <connection>scm:git:git://github.com/eclipse-ee4j/jsonp.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jsonp.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jsonp</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <licenses>
+        <license>
+            <name>Eclipse Public License 2.0</name>
+            <url>https://projects.eclipse.org/license/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GNU General Public License, version 2 with the GNU Classpath Exception</name>
+            <url>https://projects.eclipse.org/license/secondary-gpl-2.0-cp</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <developers>
+        <developer>
+            <id>m0mus</id>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>project lead</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>lukasj</id>
+            <name>Lukas Jungmann</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>dev lead</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <properties>
+        <api_package>jakarta.json</api_package>
+        <impl_namespace>org.glassfish</impl_namespace>
+        <spec_version>2.0</spec_version>
+        <new_spec_version>2.1</new_spec_version>
+        <new_spec_impl_version>2.1.0</new_spec_impl_version>
+        <impl_version>${project.version}</impl_version>
+        <new_impl_version>2.1.0</new_impl_version>
+        <non.final>false</non.final>
+        <legal.doc.source>${maven.multiModuleProjectDirectory}</legal.doc.source>
+        <config.dir>${project.root.location}/etc/config</config.dir>
+        <copyright.exclude>${config.dir}/copyright-exclude</copyright.exclude>
+        <copyright.templatefile>${config.dir}/copyright.txt</copyright.templatefile>
+        <copyright.ignoreyear>false</copyright.ignoreyear>
+        <copyright.scmonly>true</copyright.scmonly>
+        <copyright.update>false</copyright.update>
+        <spotbugs.exclude>${config.dir}/exclude.xml</spotbugs.exclude>
+        <spotbugs.skip>false</spotbugs.skip>
+        <spotbugs.threshold>Low</spotbugs.threshold>
+        <spotbugs.version>4.2.2</spotbugs.version>
+
+        <jakarta.json-api.version>2.0.1</jakarta.json-api.version>
+
+        <jakarta.annotation-api.version>2.0.0</jakarta.annotation-api.version>
+        <jakarta.xml.bind-api.version>3.0.1</jakarta.xml.bind-api.version>
+        <jakarta.ws.rs-api.version>3.0.0</jakarta.ws.rs-api.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>enforce-maven</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireJavaVersion>
+                                    <version>[11,)</version>
+                                </requireJavaVersion>
+                                <requireMavenVersion>
+                                    <version>[3.6.0,)</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.commonjava.maven.plugins</groupId>
+                <artifactId>directory-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>find-project-root</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>highest-basedir</goal>
+                        </goals>
+                        <configuration>
+                            <property>project.root.location</property>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <release>9</release>
+                    <compilerArgs>
+                        <arg>-Xlint:all</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>base-compile</id>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <release>8</release>
+                            <excludes>
+                                <exclude>module-info.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- Requires validate target to initialize copyright.config.dir properly -->
+            <!-- e.g. mvn validate glassfish-copyright:repair -->
+            <plugin>
+                <groupId>org.glassfish.copyright</groupId>
+                <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                <configuration>
+                    <templateFile>${copyright.templatefile}</templateFile>
+                    <excludeFile>${copyright.exclude}</excludeFile>
+                    <!-- skip files not under SCM-->
+                    <scmOnly>${copyright.scmonly}</scmOnly>
+                    <!-- for use with repair -->
+                    <update>${copyright.update}</update>
+                    <!-- check that year is correct -->
+                    <ignoreYear>${copyright.ignoreyear}</ignoreYear>
+                    <quiet>false</quiet>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <phase>verify</phase>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-legal-resource</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>add-resource</goal>
+                        </goals>
+                        <configuration>
+                            <resources>
+                                <resource>
+                                    <directory>${legal.doc.source}</directory>
+                                    <includes>
+                                        <include>NOTICE.md</include>
+                                        <include>LICENSE.md</include>
+                                    </includes>
+                                    <targetPath>META-INF</targetPath>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultEntries>false</addDefaultEntries>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>${release.arguments}</arguments>
+                </configuration>
+            </plugin>
+        </plugins>
+
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-enforcer-plugin</artifactId>
+                    <version>3.0.0-M3</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.commonjava.maven.plugins</groupId>
+                    <artifactId>directory-maven-plugin</artifactId>
+                    <version>0.3.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>com.github.spotbugs</groupId>
+                    <artifactId>spotbugs-maven-plugin</artifactId>
+                    <version>${spotbugs.version}</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.build</groupId>
+                    <artifactId>spec-version-maven-plugin</artifactId>
+                    <version>2.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.copyright</groupId>
+                    <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                    <version>2.4</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>5.1.1</version>
+                    <configuration>
+                        <instructions>
+                            <_noextraheaders>true</_noextraheaders>
+                        </instructions>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>3.2.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-dependency-plugin</artifactId>
+                    <version>3.1.2</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-resources-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-clean-plugin</artifactId>
+                    <version>3.1.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-site-plugin</artifactId>
+                    <version>3.9.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>3.0.0-M5</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-assembly-plugin</artifactId>
+                    <version>3.3.0</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>jakarta.ws.rs</groupId>
+                <artifactId>jakarta.ws.rs-api</artifactId>
+                <version>${jakarta.ws.rs-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.annotation</groupId>
+                <artifactId>jakarta.annotation-api</artifactId>
+                <version>${jakarta.annotation-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.json</groupId>
+                <artifactId>jakarta.json-api</artifactId>
+                <version>${jakarta.json-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.glassfish</groupId>
+                <artifactId>jakarta.json</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.glassfish</groupId>
+                <artifactId>jakarta.json</artifactId>
+                <classifier>module</classifier>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.glassfish</groupId>
+                <artifactId>jsonp-jaxrs</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.xml.bind</groupId>
+                <artifactId>jakarta.xml.bind-api</artifactId>
+                <version>${jakarta.xml.bind-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.13.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-core</artifactId>
+                <version>1.3</version>
+                <scope>test</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <modules>
+        <module>impl</module>
+        <module>jaxrs</module>
+        <module>bundles</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>all</id>
+            <dependencyManagement>
+                <!-- dependencies used by demo projects only -->
+                <dependencies>
+                    <dependency>
+                        <groupId>jakarta.servlet</groupId>
+                        <artifactId>jakarta.servlet-api</artifactId>
+                        <version>5.0.0</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>com.sun.xml.bind</groupId>
+                        <artifactId>jaxb-impl</artifactId>
+                        <version>3.0.0</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+
+            <modules>
+                <module>gf</module>
+                <module>demos</module>
+            </modules>
+
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-war-plugin</artifactId>
+                            <version>3.3.1</version>
+                            <configuration>
+                                <failOnMissingWebXml>false</failOnMissingWebXml>
+                            </configuration>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.codehaus.mojo</groupId>
+                            <artifactId>wagon-maven-plugin</artifactId>
+                            <version>2.0.2</version>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.codehaus.mojo</groupId>
+                            <artifactId>exec-maven-plugin</artifactId>
+                            <version>3.0.0</version>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <configuration>
+                    <skip>${spotbugs.skip}</skip>
+                    <threshold>${spotbugs.threshold}</threshold>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                    <excludeFilterFile>
+                        ${spotbugs.exclude}
+                    </excludeFilterFile>
+                    <fork>true</fork>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+</project>
diff --git a/spec/.gitignore b/spec/.gitignore
new file mode 100644
index 0000000..450b4b3
--- /dev/null
+++ b/spec/.gitignore
@@ -0,0 +1,2 @@
+.m2/
+target/
diff --git a/spec/LICENSE b/spec/LICENSE
new file mode 100644
index 0000000..d3087e4
--- /dev/null
+++ b/spec/LICENSE
@@ -0,0 +1,277 @@
+Eclipse Public License - v 2.0
+
+    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+    PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+  a) in the case of the initial Contributor, the initial content
+     Distributed under this Agreement, and
+
+  b) in the case of each subsequent Contributor:
+     i) changes to the Program, and
+     ii) additions to the Program;
+  where such changes and/or additions to the Program originate from
+  and are Distributed by that particular Contributor. A Contribution
+  "originates" from a Contributor if it was added to the Program by
+  such Contributor itself or anyone acting on such Contributor's behalf.
+  Contributions do not include changes or additions to the Program that
+  are not Modified Works.
+
+"Contributor" means any person or entity that Distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions Distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement
+or any Secondary License (as applicable), including Contributors.
+
+"Derivative Works" shall mean any work, whether in Source Code or other
+form, that is based on (or derived from) the Program and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship.
+
+"Modified Works" shall mean any work in Source Code or other form that
+results from an addition to, deletion from, or modification of the
+contents of the Program, including, for purposes of clarity any new file
+in Source Code form that contains any contents of the Program. Modified
+Works shall not include works that contain only declarations,
+interfaces, types, classes, structures, or files of the Program solely
+in each case in order to link to, bind by name, or subclass the Program
+or Modified Works thereof.
+
+"Distribute" means the acts of a) distributing or b) making available
+in any manner that enables the transfer of a copy.
+
+"Source Code" means the form of a Program preferred for making
+modifications, including but not limited to software source code,
+documentation source, and configuration files.
+
+"Secondary License" means either the GNU General Public License,
+Version 2.0, or any later versions of that license, including any
+exceptions or additional permissions as identified by the initial
+Contributor.
+
+2. GRANT OF RIGHTS
+
+  a) Subject to the terms of this Agreement, each Contributor hereby
+  grants Recipient a non-exclusive, worldwide, royalty-free copyright
+  license to reproduce, prepare Derivative Works of, publicly display,
+  publicly perform, Distribute and sublicense the Contribution of such
+  Contributor, if any, and such Derivative Works.
+
+  b) Subject to the terms of this Agreement, each Contributor hereby
+  grants Recipient a non-exclusive, worldwide, royalty-free patent
+  license under Licensed Patents to make, use, sell, offer to sell,
+  import and otherwise transfer the Contribution of such Contributor,
+  if any, in Source Code or other form. This patent license shall
+  apply to the combination of the Contribution and the Program if, at
+  the time the Contribution is added by the Contributor, such addition
+  of the Contribution causes such combination to be covered by the
+  Licensed Patents. The patent license shall not apply to any other
+  combinations which include the Contribution. No hardware per se is
+  licensed hereunder.
+
+  c) Recipient understands that although each Contributor grants the
+  licenses to its Contributions set forth herein, no assurances are
+  provided by any Contributor that the Program does not infringe the
+  patent or other intellectual property rights of any other entity.
+  Each Contributor disclaims any liability to Recipient for claims
+  brought by any other entity based on infringement of intellectual
+  property rights or otherwise. As a condition to exercising the
+  rights and licenses granted hereunder, each Recipient hereby
+  assumes sole responsibility to secure any other intellectual
+  property rights needed, if any. For example, if a third party
+  patent license is required to allow Recipient to Distribute the
+  Program, it is Recipient's responsibility to acquire that license
+  before distributing the Program.
+
+  d) Each Contributor represents that to its knowledge it has
+  sufficient copyright rights in its Contribution, if any, to grant
+  the copyright license set forth in this Agreement.
+
+  e) Notwithstanding the terms of any Secondary License, no
+  Contributor makes additional grants to any Recipient (other than
+  those set forth in this Agreement) as a result of such Recipient's
+  receipt of the Program under the terms of a Secondary License
+  (if permitted under the terms of Section 3).
+
+3. REQUIREMENTS
+
+3.1 If a Contributor Distributes the Program in any form, then:
+
+  a) the Program must also be made available as Source Code, in
+  accordance with section 3.2, and the Contributor must accompany
+  the Program with a statement that the Source Code for the Program
+  is available under this Agreement, and informs Recipients how to
+  obtain it in a reasonable manner on or through a medium customarily
+  used for software exchange; and
+
+  b) the Contributor may Distribute the Program under a license
+  different than this Agreement, provided that such license:
+     i) effectively disclaims on behalf of all other Contributors all
+     warranties and conditions, express and implied, including
+     warranties or conditions of title and non-infringement, and
+     implied warranties or conditions of merchantability and fitness
+     for a particular purpose;
+
+     ii) effectively excludes on behalf of all other Contributors all
+     liability for damages, including direct, indirect, special,
+     incidental and consequential damages, such as lost profits;
+
+     iii) does not attempt to limit or alter the recipients' rights
+     in the Source Code under section 3.2; and
+
+     iv) requires any subsequent distribution of the Program by any
+     party to be under a license that satisfies the requirements
+     of this section 3.
+
+3.2 When the Program is Distributed as Source Code:
+
+  a) it must be made available under this Agreement, or if the
+  Program (i) is combined with other material in a separate file or
+  files made available under a Secondary License, and (ii) the initial
+  Contributor attached to the Source Code the notice described in
+  Exhibit A of this Agreement, then the Program may be made available
+  under the terms of such Secondary Licenses, and
+
+  b) a copy of this Agreement must be included with each copy of
+  the Program.
+
+3.3 Contributors may not remove or alter any copyright, patent,
+trademark, attribution notices, disclaimers of warranty, or limitations
+of liability ("notices") contained within the Program from any copy of
+the Program which they Distribute, provided that Contributors may add
+their own appropriate notices.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program,
+the Contributor who includes the Program in a commercial product
+offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes
+the Program in a commercial product offering, such Contributor
+("Commercial Contributor") hereby agrees to defend and indemnify every
+other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits
+and other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the Program
+in a commercial product offering. The obligations in this section do not
+apply to any claims or Losses relating to any actual or alleged
+intellectual property infringement. In order to qualify, an Indemnified
+Contributor must: a) promptly notify the Commercial Contributor in
+writing of such claim, and b) allow the Commercial Contributor to control,
+and cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those performance
+claims and warranties, and if a court requires any other Contributor to
+pay any damages as a result, the Commercial Contributor must pay
+those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE. Each Recipient is solely responsible for determining the
+appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement,
+including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs
+or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software
+or hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. The Eclipse Foundation
+is the initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+Distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is published,
+Contributor may elect to Distribute the Program (including its
+Contributions) under the new version.
+
+Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+receives no rights or licenses to the intellectual property of any
+Contributor under this Agreement, whether expressly, by implication,
+estoppel or otherwise. All rights in the Program not expressly granted
+under this Agreement are reserved. Nothing in this Agreement is intended
+to be enforceable by any entity that is not a Contributor or Recipient.
+No third-party beneficiary rights are created under this Agreement.
+
+Exhibit A - Form of Secondary Licenses Notice
+
+"This Source Code may also be made available under the following 
+Secondary Licenses when the conditions for such availability set forth 
+in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+version(s), and exceptions or additional permissions here}."
+
+  Simply including a copy of this Agreement, including this Exhibit A
+  is not sufficient to license the Source Code under Secondary Licenses.
+
+  If it is not possible or desirable to put the notice in a particular
+  file, then You may include the notice in a location (such as a LICENSE
+  file in a relevant directory) where a recipient would be likely to
+  look for such a notice.
+
+  You may add additional accurate notices of copyright ownership.
diff --git a/spec/README.md b/spec/README.md
new file mode 100644
index 0000000..e25307a
--- /dev/null
+++ b/spec/README.md
@@ -0,0 +1,26 @@
+Jakarta JSON Processing Specification
+======================================
+
+This project generates the Jakarta JSON Processing Specification.
+
+Building
+--------
+
+Prerequisites:
+
+* JDK8+
+* Maven 3.0.3+
+
+Run the full build with DRAFT status:
+
+`mvn install`
+
+Run the full build with custom status (ex. "Final Release"):
+
+`mvn install -Dstatus=“Final Release”` 
+
+Locate the html files:
+- `target/generated-docs/jsonp-spec-<version>.html`
+
+Locate the PDF files:
+- `target/generated-docs/jsonp-spec-<version>.pdf`
diff --git a/spec/pom.xml b/spec/pom.xml
new file mode 100644
index 0000000..be9a379
--- /dev/null
+++ b/spec/pom.xml
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.6</version>
+        <relativePath/>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>jakarta.json</groupId>
+    <artifactId>jakarta.json-spec</artifactId>
+    <packaging>pom</packaging>
+    <version>2.0-SNAPSHOT</version>
+    <name>Jakarta JSON Processing Specification</name>
+
+    <properties>
+        <site.output.dir>${project.build.directory}/staging</site.output.dir>
+        <maven.site.skip>true</maven.site.skip>
+        <asciidoctor.maven.plugin.version>2.0.0</asciidoctor.maven.plugin.version>
+        <asciidoctorj.pdf.version>1.5.3</asciidoctorj.pdf.version>
+        <!-- status: DRAFT, BETA, etc., or blank for final -->
+        <status>DRAFT</status>
+        <maven.build.timestamp.format>MMMM dd, yyyy</maven.build.timestamp.format>
+        <revisiondate>${maven.build.timestamp}</revisiondate>
+    </properties>
+
+    <scm>
+        <connection>scm:git:git@github.com:eclipse-ee4j/jsonp.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jsonp.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jsonp</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <distributionManagement>
+        <site>
+            <url>scm:git:git@github.com:eclipse-ee4j/jsonp.git</url>
+        </site>
+    </distributionManagement>
+
+    <build>
+        <defaultGoal>package</defaultGoal>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>1.4.1</version>
+                <executions>
+                    <execution>
+                        <id>enforce-versions</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireJavaVersion>
+                                    <version>[1.8.0,)</version>
+                                    <message>You need JDK8 or higher</message>
+                                </requireJavaVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.asciidoctor</groupId>
+                <artifactId>asciidoctor-maven-plugin</artifactId>
+                <version>${asciidoctor.maven.plugin.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.asciidoctor</groupId>
+                        <artifactId>asciidoctorj-pdf</artifactId>
+                        <version>${asciidoctorj.pdf.version}</version>
+                    </dependency>
+                </dependencies>
+                <executions>
+                    <execution>
+                        <id>asciidoc-to-html</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>process-asciidoc</goal>
+                        </goals>
+                        <configuration>
+                            <backend>html5</backend>
+                            <outputFile>${project.build.directory}/generated-docs/jsonp-spec-${project.version}.html</outputFile>
+                            <attributes>
+                                <doctype>book</doctype>
+                                <status>${status}</status>
+                                <data-uri />
+                                <icons>font</icons>
+                                <toc>left</toc>
+                                <icons>font</icons>
+                                <sectanchors>true</sectanchors>
+                                <idprefix />
+                                <idseparator>-</idseparator>
+                                <docinfo1>true</docinfo1>
+                            </attributes>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>asciidoc-to-pdf</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>process-asciidoc</goal>
+                        </goals>
+                        <configuration>
+                            <backend>pdf</backend>
+                            <outputFile>${project.build.directory}/generated-docs/jsonp-spec-${project.version}.pdf</outputFile>
+                            <attributes>
+                                <pdf-stylesdir>${project.basedir}/src/theme</pdf-stylesdir>
+                                <pdf-style>jakartaee</pdf-style>
+                                <doctype>book</doctype>
+                                <status>${status}</status>
+                                <data-uri />
+                                <icons>font</icons>
+                                <pagenums />
+                                <toc />
+                                <icons>font</icons>
+                                <sectanchors>true</sectanchors>
+                                <idprefix />
+                                <idseparator>-</idseparator>
+                                <docinfo1>true</docinfo1>
+                                <embedAssets>true</embedAssets>
+                            </attributes>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <sourceDocumentName>jsonp-spec.adoc</sourceDocumentName>
+                    <sourceHighlighter>coderay</sourceHighlighter>
+                    <attributes>
+                        <revnumber>${project.version}</revnumber>
+                        <revremark>${status}</revremark>
+                        <revdate>${revisiondate}</revdate>
+                    </attributes>
+                </configuration>
+
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.2</version>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>${release.arguments}</arguments>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.scm</groupId>
+                        <artifactId>maven-scm-provider-gitexe</artifactId>
+                        <version>1.9.4</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+
+            <!--
+                This is the rule that builds the zip file for download.
+            -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>3.3.0</version>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <appendAssemblyId>false</appendAssemblyId>
+                            <descriptors>
+                                <descriptor>${project.basedir}/src/assembly/assembly.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/spec/src/assembly/assembly.xml b/spec/src/assembly/assembly.xml
new file mode 100644
index 0000000..53f9453
--- /dev/null
+++ b/spec/src/assembly/assembly.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!--
+
+    Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<assembly>
+    <id>spec</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <baseDirectory>jsonp-spec</baseDirectory>
+    <fileSets>
+        <fileSet>
+            <directory>target/generated-docs</directory>
+    	    <outputDirectory></outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png
new file mode 100644
index 0000000..97b46ce
--- /dev/null
+++ b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png
Binary files differ
diff --git a/spec/src/main/asciidoc/jsonp-spec.adoc b/spec/src/main/asciidoc/jsonp-spec.adoc
new file mode 100644
index 0000000..5077a92
--- /dev/null
+++ b/spec/src/main/asciidoc/jsonp-spec.adoc
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2017, 2020 Contributors to the Eclipse Foundation
+//
+
+= Jakarta JSON Processing
+:authors: Jakarta JSON Processing Team, https://projects.eclipse.org/projects/ee4j.jsonp
+:email: https://dev.eclipse.org/mailman/listinfo/jsonp-dev
+:version-label!:
+:doctype: book
+:license: Eclipse Foundation Specification License v1.0
+:source-highlighter: coderay
+:toc: left
+:toclevels: 4
+:sectnumlevels: 4
+:sectanchors:
+ifdef::backend-pdf[]
+:pagenums:
+:numbered:
+:title-logo-image: image:images/jakarta_ee_logo_schooner_color_stacked_default.png[pdfwidth=4.25in,align=right]
+endif::[]
+
+// == License
+:sectnums!:
+include::license-efsl.adoc[]
+
+// == Spec
+:sectnums:
+include::jsonp.adoc[]
diff --git a/spec/src/main/asciidoc/jsonp.adoc b/spec/src/main/asciidoc/jsonp.adoc
new file mode 100644
index 0000000..e33e8ff
--- /dev/null
+++ b/spec/src/main/asciidoc/jsonp.adoc
@@ -0,0 +1,7 @@
+//
+// Copyright (c) 2018, 2020 Contributors to the Eclipse Foundation
+//
+
+== Introduction
+
+Jakarta JSON Processing defines a Java(R) based framework for parsing, generating, transforming, and querying JSON documents.
diff --git a/spec/src/main/asciidoc/license-efsl.adoc b/spec/src/main/asciidoc/license-efsl.adoc
new file mode 100644
index 0000000..d900a4a
--- /dev/null
+++ b/spec/src/main/asciidoc/license-efsl.adoc
@@ -0,0 +1,79 @@
+[subs="normal"]
+....
+Specification: {doctitle}
+
+Version: {revnumber}
+
+ifeval::["{revremark}" != ""]
+Status: {revremark}
+endif::[]
+ifeval::["{revremark}" == ""]
+Status: Final Release
+endif::[]
+
+Release: {revdate}
+....
+
+Copyright (c) 2019 Eclipse Foundation.
+
+=== Eclipse Foundation Specification License
+
+By using and/or copying this document, or the Eclipse Foundation
+document from which this statement is linked, you (the licensee) agree
+that you have read, understood, and will comply with the following
+terms and conditions:
+
+Permission to copy, and distribute the contents of this document, or
+the Eclipse Foundation document from which this statement is linked, in
+any medium for any purpose and without fee or royalty is hereby
+granted, provided that you include the following on ALL copies of the
+document, or portions thereof, that you use:
+
+* link or URL to the original Eclipse Foundation document.
+* All existing copyright notices, or if one does not exist, a notice
+  (hypertext is preferred, but a textual representation is permitted)
+  of the form: "Copyright (c) [$date-of-document]
+  Eclipse Foundation, Inc. <<url to this license>>"
+
+Inclusion of the full text of this NOTICE must be provided. We
+request that authorship attribution be provided in any software,
+documents, or other items or products that you create pursuant to the
+implementation of the contents of this document, or any portion
+thereof.
+
+No right to create modifications or derivatives of Eclipse Foundation
+documents is granted pursuant to this license, except anyone may
+prepare and distribute derivative works and portions of this document
+in software that implements the specification, in supporting materials
+accompanying such software, and in documentation of such software,
+PROVIDED that all such works include the notice below. HOWEVER, the
+publication of derivative works of this document for use as a technical
+specification is expressly prohibited.
+
+The notice is:
+
+"Copyright (c) 2018 Eclipse Foundation. This software or
+document includes material copied from or derived from [title and URI
+of the Eclipse Foundation specification document]."
+
+==== Disclaimers
+
+THIS DOCUMENT IS PROVIDED &quot;AS IS,&quot; AND THE COPYRIGHT
+HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE
+SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS
+WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR
+OTHER RIGHTS.
+
+THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE
+FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT
+OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE
+CONTENTS THEREOF.
+
+The name and trademarks of the copyright holders or the Eclipse
+Foundation may NOT be used in advertising or publicity pertaining to
+this document or its contents without specific, written prior
+permission. Title to copyright in this document will at all times
+remain with copyright holders.
\ No newline at end of file
diff --git a/spec/src/theme/jakartaee-theme.yml b/spec/src/theme/jakartaee-theme.yml
new file mode 100644
index 0000000..6092a2f
--- /dev/null
+++ b/spec/src/theme/jakartaee-theme.yml
@@ -0,0 +1,299 @@
+#
+# Following is the asciidoctor-pdf default theme [1], with small
+# customizations, mostly for header and footer, marked "EE".
+#
+# [1] https://github.com/asciidoctor/asciidoctor-pdf/blob/master/data/themes/default-theme.yml
+#
+font:
+  catalog:
+    # Noto Serif supports Latin, Latin-1 Supplement, Latin Extended-A, Greek, Cyrillic, Vietnamese & an assortment of symbols
+    Noto Serif:
+      normal: notoserif-regular-subset.ttf
+      bold: notoserif-bold-subset.ttf
+      italic: notoserif-italic-subset.ttf
+      bold_italic: notoserif-bold_italic-subset.ttf
+    # M+ 1mn supports ASCII and the circled numbers used for conums
+    M+ 1mn:
+      normal: mplus1mn-regular-ascii-conums.ttf
+      bold: mplus1mn-bold-ascii.ttf
+      italic: mplus1mn-italic-ascii.ttf
+      bold_italic: mplus1mn-bold_italic-ascii.ttf
+    # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols
+    # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font
+    M+ 1p Fallback:
+      normal: mplus1p-regular-fallback.ttf
+      bold: mplus1p-regular-fallback.ttf
+      italic: mplus1p-regular-fallback.ttf
+      bold_italic: mplus1p-regular-fallback.ttf
+  fallbacks:
+    - M+ 1p Fallback
+page:
+  background_color: ffffff
+  layout: portrait
+  margin: [0.5in, 0.67in, 0.67in, 0.67in]
+  # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress
+  margin_inner: 0.75in
+  margin_outer: 0.59in
+  #size: A4                                     # EE
+  size: Letter                                  # EE
+base:
+  align: justify
+  # color as hex string (leading # is optional)
+  font_color: 333333
+  # color as RGB array
+  #font_color: [51, 51, 51]
+  # color as CMYK array (approximated)
+  #font_color: [0, 0, 0, 0.92]
+  #font_color: [0, 0, 0, 92%]
+  font_family: Noto Serif
+  # choose one of these font_size/line_height_length combinations
+  #font_size: 14
+  #line_height_length: 20
+  #font_size: 11.25
+  #line_height_length: 18
+  #font_size: 11.2
+  #line_height_length: 16
+  font_size: 10.5
+  #line_height_length: 15
+  # correct line height for Noto Serif metrics
+  line_height_length: 12
+  #font_size: 11.25
+  #line_height_length: 18
+  line_height: $base_line_height_length / $base_font_size
+  font_size_large: round($base_font_size * 1.25)
+  font_size_small: round($base_font_size * 0.85)
+  font_size_min: $base_font_size * 0.75
+  font_style: normal
+  border_color: eeeeee
+  border_radius: 4
+  border_width: 0.5
+# FIXME vertical_rhythm is weird; we should think in terms of ems
+#vertical_rhythm: $base_line_height_length * 2 / 3
+# correct line height for Noto Serif metrics (comes with built-in line height)
+vertical_rhythm: $base_line_height_length
+horizontal_rhythm: $base_line_height_length
+# QUESTION should vertical_spacing be block_spacing instead?
+vertical_spacing: $vertical_rhythm
+link:
+  font_color: 428bca
+# literal is currently used for inline monospaced in prose and table cells
+literal:
+  font_color: b12146
+  font_family: M+ 1mn
+menu_caret_content: " <font size=\"1.15em\"><color rgb=\"b12146\">\u203a</color></font> "
+heading:
+  align: left
+  #font_color: 181818
+  font_color: $base_font_color
+  font_family: $base_font_family
+  font_style: bold
+  # h1 is used for part titles (book doctype) or the doctitle (article doctype)
+  #h1_font_size: floor($base_font_size * 2.6) # EE
+  h1_font_size: floor($base_font_size * 2.5) # EE, squeeze title onto one line
+  # h2 is used for chapter titles (book doctype only)
+  h2_font_size: floor($base_font_size * 2.15)
+  h3_font_size: round($base_font_size * 1.7)
+  h4_font_size: $base_font_size_large
+  h5_font_size: $base_font_size
+  h6_font_size: $base_font_size_small
+  #line_height: 1.4
+  # correct line height for Noto Serif metrics (comes with built-in line height)
+  line_height: 1
+  margin_top: $vertical_rhythm * 0.4
+  margin_bottom: $vertical_rhythm * 0.9
+title_page:
+  align: right
+  logo:
+    top: 10%
+  title:
+    top: 55%
+    font_size: $heading_h1_font_size
+    font_color: 999999
+    line_height: 0.9
+  subtitle:
+    font_size: $heading_h3_font_size
+    font_style: bold_italic
+    line_height: 1
+  authors:
+    margin_top: $base_font_size * 1.25
+    font_size: $base_font_size_large
+    font_color: 181818
+  revision:
+    margin_top: $base_font_size * 1.25
+block:
+  margin_top: 0
+  margin_bottom: $vertical_rhythm
+caption:
+  align: left
+  font_size: $base_font_size * 0.95
+  font_style: italic
+  # FIXME perhaps set line_height instead of / in addition to margins?
+  margin_inside: $vertical_rhythm / 3
+  #margin_inside: $vertical_rhythm / 4
+  margin_outside: 0
+lead:
+  font_size: $base_font_size_large
+  line_height: 1.4
+abstract:
+  font_color: 5c6266
+  font_size: $lead_font_size
+  line_height: $lead_line_height
+  font_style: italic
+  first_line_font_style: bold
+  title:
+    align: center
+    font_color: $heading_font_color
+    font_family: $heading_font_family
+    font_size: $heading_h4_font_size
+    font_style: $heading_font_style
+admonition:
+  column_rule_color: $base_border_color
+  column_rule_width: $base_border_width
+  padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm]
+  #icon:
+  #  tip:
+  #    name: fa-lightbulb-o
+  #    stroke_color: 111111
+  #    size: 24
+  label:
+    text_transform: uppercase
+    font_style: bold
+blockquote:
+  font_color: $base_font_color
+  font_size: $base_font_size_large
+  border_color: $base_border_color
+  border_width: 5
+  # FIXME disable negative padding bottom once margin collapsing is implemented
+  padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2]
+  cite_font_size: $base_font_size_small
+  cite_font_color: 999999
+# code is used for source blocks (perhaps change to source or listing?)
+code:
+  font_color: $base_font_color
+  font_family: $literal_font_family
+  font_size: ceil($base_font_size)
+  padding: $code_font_size
+  line_height: 1.25
+  # line_gap is an experimental property to control how a background color is applied to an inline block element
+  line_gap: 3.8
+  background_color: f5f5f5
+  border_color: cccccc
+  border_radius: $base_border_radius
+  border_width: 0.75
+conum:
+  font_family: M+ 1mn
+  font_color: $literal_font_color
+  font_size: $base_font_size
+  line_height: 4 / 3
+example:
+  border_color: $base_border_color
+  border_radius: $base_border_radius
+  border_width: 0.75
+  background_color: ffffff
+  # FIXME reenable padding bottom once margin collapsing is implemented
+  padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm]
+image:
+  align: left
+prose:
+  margin_top: $block_margin_top
+  margin_bottom: $block_margin_bottom
+sidebar:
+  background_color: eeeeee
+  border_color: e1e1e1
+  border_radius: $base_border_radius
+  border_width: $base_border_width
+  # FIXME reenable padding bottom once margin collapsing is implemented
+  padding: [$vertical_rhythm, $vertical_rhythm * 1.25, 0, $vertical_rhythm * 1.25]
+  title:
+    align: center
+    font_color: $heading_font_color
+    font_family: $heading_font_family
+    font_size: $heading_h4_font_size
+    font_style: $heading_font_style
+thematic_break:
+  border_color: $base_border_color
+  border_style: solid
+  border_width: $base_border_width
+  margin_top: $vertical_rhythm * 0.5
+  margin_bottom: $vertical_rhythm * 1.5
+description_list:
+  term_font_style: bold
+  term_spacing: $vertical_rhythm / 4
+  description_indent: $horizontal_rhythm * 1.25
+outline_list:
+  indent: $horizontal_rhythm * 1.5
+  #marker_font_color: 404040
+  # NOTE outline_list_item_spacing applies to list items that do not have complex content
+  item_spacing: $vertical_rhythm / 2
+table:
+  background_color: $page_background_color
+  #head_background_color: <hex value>
+  #head_font_color: $base_font_color
+  head_font_style: bold
+  #body_background_color: <hex value>
+  body_stripe_background_color: f9f9f9
+  foot_background_color: f0f0f0
+  border_color: dddddd
+  border_width: $base_border_width
+  cell_padding: 3
+toc:
+  indent: $horizontal_rhythm
+  line_height: 1.4
+  dot_leader:
+    #content: ". "
+    font_color: a9a9a9
+    #levels: 2 3
+# NOTE in addition to footer, header is also supported
+footer:
+  font_size: $base_font_size_small
+  # NOTE if background_color is set, background and border will span width of page
+  #border_color: dddddd                         # EE
+  #border_width: 0.25                           # EE
+  height: $base_line_height_length * 2.5
+  line_height: 1
+  padding: [$base_line_height_length / 2, 1, 0, 1]
+  vertical_align: top
+  #image_vertical_align: <alignment> or <number>
+  # additional attributes for content:
+  # * {page-count}
+  # * {page-number}
+  # * {document-title}
+  # * {document-subtitle}
+  # * {chapter-title}
+  # * {section-title}
+  # * {section-or-chapter-title}
+  recto:
+    #columns: "<50% =0% >50%"
+    right:
+      #content: '{page-number}'                 # EE
+      #content: '{section-or-chapter-title} | {page-number}'
+      #content: '{document-title} | {page-number}'
+      content: '{document-title}{nbsp}{nbsp}{nbsp} *{page-number}*' # EE
+    #center:
+    #  content: '{page-number}'
+    left:                                       # EE
+      content: '{status}'                       # EE
+  verso:
+    #columns: $footer_recto_columns
+    left:
+      #content: $footer_recto_right_content     # EE
+      #content: '{page-number} | {chapter-title}'
+      content: '*{page-number}* {nbsp}{nbsp}{nbsp}{document-title}' # EE
+    #center:
+    #  content: '{page-number}'
+    right:                                      # EE
+      content: '{status}'                       # EE
+header:                                         # EE
+  font_size: $base_font_size_small              # EE
+  border_color: dddddd                          # EE
+  border_width: 0.25                            # EE
+  height: $base_line_height_length * 2.5        # EE
+  line_height: 1                                # EE
+  padding: [$base_line_height_length / 2, 1, 0, 1] # EE
+  vertical_align: top                           # EE
+  recto:                                        # EE
+    right:                                      # EE
+      content: '{section-or-chapter-title}'     # EE
+  verso:                                        # EE
+    left:                                       # EE
+      content: '{section-or-chapter-title}'     # EE
diff --git a/tck/pom.xml b/tck/pom.xml
new file mode 100644
index 0000000..dd7e80d
--- /dev/null
+++ b/tck/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.6</version>
+    </parent>
+
+    <groupId>jakarta.json</groupId>
+    <artifactId>jakarta.json-tck</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>tck-common</module>
+        <module>tck-tests</module>
+        <module>tck-tests-plugability</module>
+    </modules>
+
+    <name>JSON-P TCK</name>
+    <description>Jakarta JSON-P TCK</description>
+
+    <licenses>
+        <license>
+            <name>Eclipse Public License 2.0</name>
+            <url>https://projects.eclipse.org/license/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GNU General Public License, version 2 with the GNU Classpath Exception</name>
+            <url>https://projects.eclipse.org/license/secondary-gpl-2.0-cp</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <version>2.0.0-RC1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.inject</groupId>
+            <artifactId>jakarta.inject-api</artifactId>
+            <version>1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.junit</groupId>
+            <artifactId>arquillian-junit-container</artifactId>
+            <version>1.6.0.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tck/tck-common/pom.xml b/tck/tck-common/pom.xml
new file mode 100644
index 0000000..50d6c67
--- /dev/null
+++ b/tck/tck-common/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>jakarta.json</groupId>
+        <artifactId>jakarta.json-tck</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jakarta.json-tck-common</artifactId>
+    <packaging>jar</packaging>
+
+</project>
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Data.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Data.java
new file mode 100644
index 0000000..e7da0b1
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Data.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import java.util.*;
+
+public final class JSONP_Data {
+
+  public static final String unicodeControlCharsEscaped = "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u0008\\u0009"
+      + "\\u000a\\u000b\\u000c\\u000d\\u000e\\u000f"
+      + "\\u000A\\u000B\\u000C\\u000D\\u000E\\u000F"
+      + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019"
+      + "\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u007f"
+      + "\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\\u007F"
+      + "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087\\u0088\\u0089"
+      + "\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f"
+      + "\\u008A\\u008B\\u008C\\u008D\\u008E\\u008F"
+      + "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097\\u0098\\u0099"
+      + "\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f"
+      + "\\u009A\\u009B\\u009C\\u009D\\u009E\\u009F";
+
+  // NOTE: For the unicode values u000a and u000d we need to use the Java
+  // escape for both NL and CR as \n and \r respectively
+  public static final String unicodeControlCharsNonEscaped = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009"
+      + "\n\u000b\u000c\r\u000e\u000f" + "\n\u000B\u000C\r\u000E\u000F"
+      + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019"
+      + "\u001a\u001b\u001c\u001d\u001e\u001f\u007f"
+      + "\u001A\u001B\u001C\u001D\u001E\u001F\u007F"
+      + "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089"
+      + "\u008a\u008b\u008c\u008d\u008e\u008f"
+      + "\u008A\u008B\u008C\u008D\u008E\u008F"
+      + "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099"
+      + "\u009a\u009b\u009c\u009d\u009e\u009f"
+      + "\u009A\u009B\u009C\u009D\u009E\u009F";
+
+  // Standard backslash escape characters
+  public static final String escapeCharsAsString = "\"\\/\b\f\n\r\t";
+
+  public static final String asciiCharacters = "!@#$%^&*()_+|~1234567890-=;',./<>? "
+      + "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
+
+  public static final String jsonArrayTestData = "[ \"\", \"string\", 100, true, false, null, {}, { \"name\" : \"value\" }, [], [ \"one\", \"two\" ] ]";
+
+  public static final String jsonObjectTestData = "{"
+      + "\"emptyString\" : \"\"," + "\"emptyArray\" : [],"
+      + "\"emptyObject\" : {}," + "\"string\" : \"string\","
+      + "\"number\" :  100," + "\"true\" : true," + "\"false\" : false,"
+      + "\"null\" : null," + "\"object\" : { \"name\" : \"value\" },"
+      + "\"array\" : [ \"one\", \"two\" ]," + "}";
+
+  public static final String jsonArrayTestData2 = "[" + "\"\"," + "[]," + "{},"
+      + "\"string\"," + "100," + "true," + "false," + "null,"
+      + "{ \"name\" : \"value\" }," + "[ \"one\", \"two\" ]," + "]";
+
+  public static final String jsonObjectWithAllTypesOfData = "{"
+      + "\"emptyString\" : \"\"," + "\"emptyArray\" : [],"
+      + "\"emptyObject\" : {}," + "\"string\" : \"string\","
+      + "\"number\" :  100," + "\"true\" : true," + "\"false\" : false,"
+      + "\"null\" : null," + "\"object\" : {" + "\"emptyString\" : \"\","
+      + "\"emptyArray\" : []," + "\"emptyObject\" : {},"
+      + "\"string\" : \"string\"," + "\"number\" :  100," + "\"true\" : true,"
+      + "\"false\" : false," + "\"null\" : null,"
+      + "\"object\" : { \"name\" : \"value\" },"
+      + "\"array\" : [ \"one\", \"two\" ]" + "},"
+      + "\"array\" : [ \"string\", 100, true, false, null, { \"name\" : \"value\" }"
+      + ", [ \"one\", \"two\" ] " + "]," + "\"intPositive\" : 100,"
+      + "\"intNegative\" : -100," + "\"longMax\"     : 9223372036854775807,"
+      + "\"longMin\"     : -9223372036854775808," + "\"fracPositive\" : 0.5,"
+      + "\"fracNegative\" : -0.5," + "\"expPositive1\" : 7e3,"
+      + "\"expPositive2\" : 7e+3," + "\"expPositive3\" : 9E3,"
+      + "\"expPositive4\" : 9E+3," + "\"expNegative1\" : 7e-3,"
+      + "\"expNegative2\" : 7E-3," + "\"asciiChars\" : \"" + asciiCharacters
+      + "\"" + "}";
+
+  public static final String jsonArrayWithAllTypesOfData = "[" + "\"\"," + "[],"
+      + "{}," + "\"string\"," + "100," + "true," + "false," + "null," + "{"
+      + "\"emptyString\" : \"\"," + "\"emptyArray\" : [],"
+      + "\"emptyObject\" : {}," + "\"string\" : \"string\","
+      + "\"number\" :  100," + "\"true\" : true," + "\"false\" : false,"
+      + "\"null\" : null," + "\"object\" : { \"name\" : \"value\" },"
+      + "\"array\" : [ \"one\", \"two\" ]" + "},"
+      + "[ \"string\", 100, true, false, null, { \"name\" : \"value\" }"
+      + ", [ \"one\", \"two\" ] " + "]," + "100," + "-100,"
+      + "9223372036854775807," + "-9223372036854775808," + "0.5," + "-0.5,"
+      + "7e3," + "7e+3," + "9E3," + "9E+3," + "7e-3," + "7E-3," + "\""
+      + asciiCharacters + "\"" + "]";
+
+  public static final String jsonObjectWithLotsOfNestedObjectsData = "{"
+      + "\"nested1\" : {" + "\"name1\" : \"value1\"," + "\"nested2\" : {"
+      + "\"name2\" : \"value2\"," + "\"nested3\" : {"
+      + "\"name3\" : \"value3\"," + "\"nested4\" : {"
+      + "\"name4\" : \"value4\"," + "\"nested5\" : {"
+      + "\"name5\" : \"value5\"," + "\"nested6\" : {"
+      + "\"name6\" : \"value6\"," + "\"nested7\" : {"
+      + "\"name7\" : \"value7\"," + "\"nested8\" : {"
+      + "\"name8\" : \"value8\"," + "\"nested9\" : {"
+      + "\"name9\" : \"value9\"," + "\"nested10\" : {"
+      + "\"name10\" : \"value10\"," + "\"nested11\" : {"
+      + "\"name11\" : \"value11\"," + "\"nested12\" : {"
+      + "\"name12\" : \"value12\"," + "\"nested13\" : {"
+      + "\"name13\" : \"value13\"," + "\"nested14\" : {"
+      + "\"name14\" : \"value14\"," + "\"nested15\" : {"
+      + "\"name15\" : \"value15\"," + "\"nested16\" : {"
+      + "\"name16\" : \"value16\"," + "\"nested17\" : {"
+      + "\"name17\" : \"value17\"," + "\"nested18\" : {"
+      + "\"name18\" : \"value18\"," + "\"nested19\" : {"
+      + "\"name19\" : \"value19\"," + "\"nested20\" : {"
+      + "\"name20\" : \"value20\"," + "\"nested21\" : {"
+      + "\"name21\" : \"value21\"," + "\"nested22\" : {"
+      + "\"name22\" : \"value22\"," + "\"nested23\" : {"
+      + "\"name23\" : \"value23\"," + "\"nested24\" : {"
+      + "\"name24\" : \"value24\"," + "\"nested25\" : {"
+      + "\"name25\" : \"value25\"," + "\"nested26\" : {"
+      + "\"name26\" : \"value26\"," + "\"nested27\" : {"
+      + "\"name27\" : \"value27\"," + "\"nested28\" : {"
+      + "\"name28\" : \"value28\"," + "\"nested29\" : {"
+      + "\"name29\" : \"value29\"," + "\"nested30\" : {"
+      + "\"name30\" : \"value30\"" + "}" + "}" + "}" + "}" + "}" + "}" + "}"
+      + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}"
+      + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}";
+
+  public static final String jsonArrayWithLotsOfNestedObjectsData = "[" + "{"
+      + "\"name1\" : \"value1\"," + "\"nested2\" : {"
+      + "\"name2\" : \"value2\"," + "\"nested3\" : {"
+      + "\"name3\" : \"value3\"," + "\"nested4\" : {"
+      + "\"name4\" : \"value4\"," + "\"nested5\" : {"
+      + "\"name5\" : \"value5\"," + "\"nested6\" : {"
+      + "\"name6\" : \"value6\"," + "\"nested7\" : {"
+      + "\"name7\" : \"value7\"," + "\"nested8\" : {"
+      + "\"name8\" : \"value8\"," + "\"nested9\" : {"
+      + "\"name9\" : \"value9\"," + "\"nested10\" : {"
+      + "\"name10\" : \"value10\"," + "\"nested11\" : {"
+      + "\"name11\" : \"value11\"," + "\"nested12\" : {"
+      + "\"name12\" : \"value12\"," + "\"nested13\" : {"
+      + "\"name13\" : \"value13\"," + "\"nested14\" : {"
+      + "\"name14\" : \"value14\"," + "\"nested15\" : {"
+      + "\"name15\" : \"value15\"," + "\"nested16\" : {"
+      + "\"name16\" : \"value16\"," + "\"nested17\" : {"
+      + "\"name17\" : \"value17\"," + "\"nested18\" : {"
+      + "\"name18\" : \"value18\"," + "\"nested19\" : {"
+      + "\"name19\" : \"value19\"," + "\"nested20\" : {"
+      + "\"name20\" : \"value20\"," + "\"nested21\" : {"
+      + "\"name21\" : \"value21\"," + "\"nested22\" : {"
+      + "\"name22\" : \"value22\"," + "\"nested23\" : {"
+      + "\"name23\" : \"value23\"," + "\"nested24\" : {"
+      + "\"name24\" : \"value24\"," + "\"nested25\" : {"
+      + "\"name25\" : \"value25\"," + "\"nested26\" : {"
+      + "\"name26\" : \"value26\"," + "\"nested27\" : {"
+      + "\"name27\" : \"value27\"," + "\"nested28\" : {"
+      + "\"name28\" : \"value28\"," + "\"nested29\" : {"
+      + "\"name29\" : \"value29\"," + "\"nested30\" : {"
+      + "\"name30\" : \"value30\"" + "}" + "}" + "}" + "}" + "}" + "}" + "}"
+      + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}"
+      + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "}" + "]";
+
+  public static final String jsonArrayWithMultipleArraysData = "[ \"string\", 100, true, false, null, { \"object\" : \"object\" }, [ \"one\","
+      + "\"two\" ], [ 100, 7e7, true, false, null, { \"object2\" : \"object2\" } ] ]";
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Util.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Util.java
new file mode 100644
index 0000000..94145de
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/JSONP_Util.java
@@ -0,0 +1,2613 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import java.lang.reflect.*;
+import java.nio.charset.Charset;
+
+import java.math.BigInteger;
+import java.math.BigDecimal;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import jakarta.json.stream.JsonParser.Event.*;
+import jakarta.json.JsonValue.ValueType.*;
+
+public final class JSONP_Util {
+
+  // Charset CONSTANTS for all the supported UTF encodings
+  public static final Charset UTF_8 = Charset.forName("UTF-8");
+
+  public static final Charset UTF_16 = Charset.forName("UTF-16");
+
+  public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
+
+  public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
+
+  public static final Charset UTF_32BE = Charset.forName("UTF-32BE");
+
+  public static final Charset UTF_32LE = Charset.forName("UTF-32LE");
+
+  // Test Config properties
+  public static final String FOO_CONFIG = "jakarta.jsonp.tck.common.FOO_CONFIG";
+
+  // Number of parser errors encountered
+  private static int parseErrs = 0;
+
+  // A JsonNumber NumberType is now INTEGRAL or NON_INTEGRAL based on
+  // JsonNumber.isIntegral().
+  // The following 2 constant definitions represent these NumberType boolean
+  // values.
+  public static final boolean INTEGRAL = true;
+
+  public static final boolean NON_INTEGRAL = false;
+
+  /*********************************************************************************
+   * void dumpContentsOfResource(String resource)
+   *********************************************************************************/
+  public static void dumpContentsOfResource(String resource) {
+    System.out.println("Dumping contents of Resource file: " + resource);
+    BufferedReader reader = null;
+    try {
+      InputStream iStream = JSONP_Util.class
+          .getResourceAsStream("/" + resource);
+      if (iStream == null) {
+        System.err.println(
+            "dumpContentsOfResource: no resource found in classpath or archive named "
+                + resource);
+        return;
+      }
+      reader = new BufferedReader(new InputStreamReader(iStream));
+      String thisLine;
+      while ((thisLine = reader.readLine()) != null) {
+        System.out.println(thisLine);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      if (reader != null)
+        try {
+          reader.close();
+        } catch (Exception e) {
+          System.err.println("exception closing stream: " + e);
+        }
+    }
+  }
+
+  /*********************************************************************************
+   * void dumpFile(String file)
+   *********************************************************************************/
+  public static void dumpFile(String file) {
+    System.out.println("Dumping contents of file: " + file);
+    BufferedReader reader = null;
+    try {
+      FileInputStream fis = new FileInputStream(file);
+      if (fis == null) {
+        System.err.println("dumpFile: no file found named " + file);
+        return;
+      }
+      reader = new BufferedReader(new InputStreamReader(fis));
+      String thisLine;
+      while ((thisLine = reader.readLine()) != null) {
+        System.out.println(thisLine);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      if (reader != null)
+        try {
+          reader.close();
+        } catch (Exception e) {
+          System.err.println("exception closing stream: " + e);
+        }
+    }
+  }
+
+  /*********************************************************************************
+   * String getContentsOfResourceAsString(String resource)
+   *********************************************************************************/
+  public static String getContentsOfResourceAsString(String resource) {
+    StringBuilder sb = new StringBuilder();
+    BufferedReader reader = null;
+    try {
+      InputStream iStream = JSONP_Util.class
+          .getResourceAsStream("/" + resource);
+      if (iStream == null) {
+        System.err.println(
+            "dumpContentsOfResource: no resource found in classpath or archive named "
+                + resource);
+        return null;
+      }
+      reader = new BufferedReader(new InputStreamReader(iStream));
+      String thisLine;
+      while ((thisLine = reader.readLine()) != null) {
+        sb.append(thisLine);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      if (reader != null)
+        try {
+          reader.close();
+        } catch (Exception e) {
+          System.err.println("exception closing stream: " + e);
+        }
+    }
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * Reader getReaderFromResource(String resource)
+   *********************************************************************************/
+  public static Reader getReaderFromResource(String resource) {
+    InputStreamReader reader = null;
+    try {
+      InputStream iStream = JSONP_Util.class
+          .getResourceAsStream("/" + resource);
+      if (iStream == null)
+        System.err.println(
+            "getReaderFromResource: no resource found in classpath or archive named "
+                + resource);
+      else
+        reader = new InputStreamReader(iStream);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return reader;
+  }
+
+  /*********************************************************************************
+   * Reader getReaderFromString(String contents)
+   *********************************************************************************/
+  public static Reader getReaderFromString(String contents) {
+    InputStreamReader reader = null;
+    try {
+      InputStream iStream = new ByteArrayInputStream(contents.getBytes(UTF_8));
+      if (iStream == null)
+        System.err.println("getReaderFromString: no input stream");
+      else
+        reader = new InputStreamReader(iStream);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return reader;
+  }
+
+  /*********************************************************************************
+   * InputStream getInputStreamFromResource(String resource)
+   *********************************************************************************/
+  public static InputStream getInputStreamFromResource(String resource) {
+    InputStream iStream = null;
+    try {
+      iStream = JSONP_Util.class.getResourceAsStream("/" + resource);
+      if (iStream == null)
+        System.err.println(
+            "getInputStreamFromResource: no resource found in classpath or archive named "
+                + resource);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return iStream;
+  }
+
+  /*********************************************************************************
+   * InputStream getInputStreamFromString(String contents)
+   *********************************************************************************/
+  public static InputStream getInputStreamFromString(String contents) {
+    InputStream iStream = null;
+    try {
+      iStream = new ByteArrayInputStream(contents.getBytes(UTF_8));
+      if (iStream == null)
+        System.err.println("getInputStreamFromString: no input stream");
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return iStream;
+  }
+
+  /*********************************************************************************
+   * InputStream getInputStreamFromOutputStream(ByteArrayOutputStream baos)
+   *********************************************************************************/
+  public static InputStream getInputStreamFromOutputStream(
+      ByteArrayOutputStream baos) {
+    InputStream iStream = null;
+    try {
+      iStream = new ByteArrayInputStream(baos.toByteArray());
+      if (iStream == null)
+        System.err.println("getInputStreamFromOutputStream: no input stream");
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return iStream;
+  }
+
+  /*********************************************************************************
+   * String removeWhitespace(String text)
+   *
+   * NOTE: This does not remove whitespace of Json text if a quoted string which
+   * can include whitespace.
+   *********************************************************************************/
+  public static String removeWhitespace(String text) {
+    StringReader reader = new StringReader(text);
+    StringWriter writer = new StringWriter();
+    try {
+      boolean quotedString = false;
+      boolean backslash = false;
+      int c;
+      while ((c = reader.read()) != -1) {
+        // Skip white space if not quoted string
+        if (!quotedString) {
+          if (Character.isWhitespace(c))
+            continue;
+        }
+        writer.write(c);
+        if (c == '"') {
+          if (!backslash)
+            quotedString = !quotedString;
+          backslash = false;
+        } else if (c == '\\')
+          backslash = true;
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return writer.toString();
+  }
+
+  /*********************************************************************************
+   * JsonNumber createJsonNumber
+   *
+   * The following method signatures are available:
+   *
+   * o JsonNumber createJsonNumber(double) o JsonNumber createJsonNumber(long) o
+   * JsonNumber createJsonNumber(int) o JsonNumber createJsonNumber(BigDecimal)
+   * o JsonNumber createJsonNumber(BigInteger)
+   *********************************************************************************/
+  public static JsonNumber createJsonNumber(double val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonNumber(0);
+  }
+
+  public static JsonNumber createJsonNumber(long val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonNumber(0);
+  }
+
+  public static JsonNumber createJsonNumber(int val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonNumber(0);
+  }
+
+  public static JsonNumber createJsonNumber(BigDecimal val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonNumber(0);
+  }
+
+  public static JsonNumber createJsonNumber(BigInteger val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonNumber(0);
+  }
+
+  /*********************************************************************************
+   * JsonString createJsonString(String val)
+   *********************************************************************************/
+  public static JsonString createJsonString(String val) {
+    JsonArray array = Json.createArrayBuilder().add(val).build();
+    return array.getJsonString(0);
+  }
+
+  /*********************************************************************************
+   * void dumpJsonString(JsonString val)
+   *********************************************************************************/
+  public static void dumpJsonString(JsonString value) {
+    System.out.println("dumpJsonString->" + toStringJsonString(value));
+  }
+
+  /*********************************************************************************
+   * void dumpJsonArray(JsonArray value)
+   *********************************************************************************/
+  public static void dumpJsonArray(JsonArray value) {
+    System.out.println("dumpJsonArray->" + toStringJsonArray(value));
+  }
+
+  /*********************************************************************************
+   * void dumpJsonObject(JsonObject value)
+   *********************************************************************************/
+  public static void dumpJsonObject(JsonObject value) {
+    System.out.println("dumpJsonObject->" + toStringJsonObject(value));
+  }
+
+  /*********************************************************************************
+   * void dumpJsonConstant(JsonValue value)
+   *********************************************************************************/
+  public static void dumpJsonConstant(JsonValue value) {
+    System.out.println("dumpJsonConstant->" + toStringJsonConstant(value));
+  }
+
+  /*********************************************************************************
+   * void dumpJsonNumber(JsonNumber value)
+   *********************************************************************************/
+  public static void dumpJsonNumber(JsonNumber value) {
+    System.out.println("dumpJsonNumber->" + toStringJsonNumber(value));
+  }
+
+  /*********************************************************************************
+   * void dumpJsonValue(JsonValue value)
+   *********************************************************************************/
+  public static void dumpJsonValue(JsonValue value) {
+    if (value instanceof JsonNumber) {
+      dumpJsonNumber((JsonNumber) value);
+    } else if (value instanceof JsonString) {
+      dumpJsonString((JsonString) value);
+    } else if (value instanceof JsonArray) {
+      dumpJsonArray((JsonArray) value);
+    } else if (value instanceof JsonObject) {
+      dumpJsonObject((JsonObject) value);
+    } else
+      dumpJsonConstant(value);
+  }
+
+  /*********************************************************************************
+   * String toStringJsonString(JsonString value)
+   *********************************************************************************/
+  public static String toStringJsonString(JsonString value) {
+    if (value == null)
+      return ("JsonString is null");
+    return ("\"" + value.getString() + "\"");
+  }
+
+  /*********************************************************************************
+   * String toStringJsonArray(JsonArray value)
+   *********************************************************************************/
+  public static String toStringJsonArray(JsonArray value) {
+    if (value == null)
+      return ("JsonArray is null");
+    StringBuilder sb = new StringBuilder();
+    sb.append("[");
+    Iterator<JsonValue> iter = value.iterator();
+    String comma = "";
+    while (iter.hasNext()) {
+      sb.append(comma + toStringJsonValue(iter.next()));
+      if (comma.equals(""))
+        comma = ",";
+    }
+    sb.append("]");
+    return (sb.toString());
+  }
+
+  /*********************************************************************************
+   * String toStringJsonObject(JsonObject value)
+   *********************************************************************************/
+  public static String toStringJsonObject(JsonObject value) {
+    if (value == null)
+      return ("JsonObject is null");
+    StringBuilder sb = new StringBuilder();
+    sb.append("{");
+    String comma = "";
+    for (Map.Entry<String, JsonValue> entry : value.entrySet()) {
+      sb.append(comma + "\"" + entry.getKey() + "\":"
+          + toStringJsonValue(entry.getValue()));
+      if (comma.equals(""))
+        comma = ",";
+    }
+    sb.append("}");
+    return (sb.toString());
+  }
+
+  /*********************************************************************************
+   * String toStringJsonConstant(JsonValue value)
+   *********************************************************************************/
+  public static String toStringJsonConstant(JsonValue value) {
+    if (value == null)
+      return ("JsonValue is null");
+    if (value == JsonValue.FALSE)
+      return "false";
+    else if (value == JsonValue.TRUE)
+      return "true";
+    else if (value == JsonValue.NULL)
+      return "null";
+    else
+      return "UNKNOWN";
+  }
+
+  /*********************************************************************************
+   * String toStringJsonNumber(JsonNumber value)
+   *********************************************************************************/
+  public static String toStringJsonNumber(JsonNumber value) {
+    if (value == null)
+      return ("JsonNumber is null");
+    if (value.isIntegral() == INTEGRAL)
+      return ("" + value.longValue());
+    else
+      return ("" + value.bigDecimalValue());
+  }
+
+  /*********************************************************************************
+   * String toStringJsonValue(JsonValue value)
+   *********************************************************************************/
+  public static String toStringJsonValue(JsonValue value) {
+    if (value instanceof JsonNumber) {
+      return toStringJsonNumber((JsonNumber) value);
+    } else if (value instanceof JsonString) {
+      return toStringJsonString((JsonString) value);
+    } else if (value instanceof JsonArray) {
+      return toStringJsonArray((JsonArray) value);
+    } else if (value instanceof JsonObject) {
+      return toStringJsonObject((JsonObject) value);
+    } else
+      return toStringJsonConstant(value);
+  }
+
+  /*********************************************************************************
+   * void dumpSet(Set<String> set, String msg)
+   *********************************************************************************/
+  public static void dumpSet(Set<String> set, String msg) {
+    System.out.println("*** Beg: Dumping List contents ***");
+    if (msg != null)
+      System.out.println("*** Message: " + msg);
+    Iterator iterator = set.iterator();
+    System.out.println("Set: (");
+    while (iterator.hasNext()) {
+      System.out.println((String) iterator.next());
+    }
+    System.out.println(")");
+    System.out.println("*** End: Dumping Set contents ***");
+  }
+
+  /*********************************************************************************
+   * void dumpSet(Set<String> set)
+   *********************************************************************************/
+  public static void dumpSet(Set<String> set) {
+    dumpSet(set, null);
+  }
+
+  /*********************************************************************************
+   * String toStringSet(Set<String> set)
+   *********************************************************************************/
+  public static String toStringSet(Set<String> set) {
+    Iterator<String> iter = set.iterator();
+    StringBuilder sb = new StringBuilder();
+    sb.append("Set: (");
+    while (iter.hasNext()) {
+      sb.append(iter.next());
+      if (iter.hasNext())
+        sb.append(",");
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsSet(Set<String>expSet, Set<String>actSet)
+   *********************************************************************************/
+  public static boolean assertEqualsSet(Set<String> expSet,
+      Set<String> actSet) {
+    if (actSet.equals(expSet)) {
+      System.out.println("Sets are equal - match (Success)");
+      System.out.println("Expected: " + toStringSet(expSet));
+      System.out.println("Actual:   " + toStringSet(actSet));
+      return true;
+    } else {
+      System.out.println("Sets are not equal - mismatch (Failure)");
+      System.err.println("Expected: " + toStringSet(expSet));
+      System.err.println("Actual:   " + toStringSet(actSet));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * void dumpMap(Map<String,JsonValue> map, String msg)
+   *********************************************************************************/
+  public static void dumpMap(Map<String, JsonValue> map, String msg) {
+    System.out.println("*** Beg: Dumping Map contents ***");
+    if (msg != null)
+      System.out.println("*** Message: " + msg);
+    System.out.println("Map: {");
+    for (Map.Entry<String, JsonValue> entry : map.entrySet()) {
+      System.out.println(
+          "\"" + entry.getKey() + "\":" + toStringJsonValue(entry.getValue()));
+    }
+    System.out.println("}");
+    System.out.println("*** End: Dumping Map contents ***");
+  }
+
+  /*********************************************************************************
+   * void dumpMap(Map<String,JsonValue> map)
+   *********************************************************************************/
+  public static void dumpMap(Map<String, JsonValue> map) {
+    dumpMap(map, null);
+  }
+
+  /*********************************************************************************
+   * String toStringMap(Map<String,JsonValue> map)
+   *********************************************************************************/
+  public static String toStringMap(Map<String, JsonValue> map) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("Map: {");
+    String comma = "";
+    for (Map.Entry<String, JsonValue> entry : map.entrySet()) {
+      sb.append(comma + "\"" + entry.getKey() + "\":"
+          + toStringJsonValue(entry.getValue()));
+      if (comma.equals(""))
+        comma = ",";
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsMap(Map<String,JsonValue>expMap,
+   * Map<String,JsonValue>actMap)
+   *********************************************************************************/
+  public static boolean assertEqualsMap(Map<String, JsonValue> expMap,
+      Map<String, JsonValue> actMap) {
+    if (actMap.equals(expMap)) {
+      System.out.println("Maps are equal - match (Success)");
+      System.out.println("Expected: " + toStringMap(expMap));
+      System.out.println("Actual:   " + toStringMap(actMap));
+      return true;
+    } else {
+      System.out.println("Maps are not equal - mismatch (Failure)");
+      System.err.println("Expected: " + toStringMap(expMap));
+      System.err.println("Actual:   " + toStringMap(actMap));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * assertEqualsMap2
+   *********************************************************************************/
+  public static boolean assertEqualsMap2(Map<String, JsonValue> expMap,
+      Map<String, JsonValue> actMap) {
+    System.out.println("*** Comparing Map expMap and Map actMap for equality ***");
+    System.out.println("Expected: " + toStringMap(expMap));
+    System.out.println("Actual:   " + toStringMap(actMap));
+    System.out.println("Map expMap size should equal Map actMap size");
+    if (expMap.size() != actMap.size()) {
+      System.err.println("Map sizes are not equal: expMap size " + expMap.size()
+          + ", actMap size " + actMap.size());
+      return false;
+    } else {
+      System.out.println("Map sizes are equal with size of " + expMap.size());
+    }
+    for (Map.Entry<String, ?> entry : expMap.entrySet()) {
+      String key = entry.getKey();
+      if (actMap.containsKey(key)) {
+        if (expMap.get(key) != null && actMap.get(key) != null) {
+          if (!expMap.get(key).equals(actMap.get(key))) {
+            System.err.println("key=" + key + ", expMap value " + expMap.get(key)
+                + " does not equal actMap value " + actMap.get(key));
+            return false;
+          }
+        }
+      } else {
+        System.err.println("actMap does not contain key " + key);
+        return false;
+      }
+    }
+    System.out.println("Maps expMap and actMap are equal.");
+    return true;
+  }
+
+  /*********************************************************************************
+   * void dumpList(List<JsonValue> list, String msg)
+   *********************************************************************************/
+  public static void dumpList(List<JsonValue> list, String msg) {
+    System.out.println("*** Beg: Dumping List contents ***");
+    if (msg != null)
+      System.out.println("*** Message: " + msg);
+    Iterator<JsonValue> iter = list.iterator();
+    System.out.println("List: [");
+    while (iter.hasNext()) {
+      System.out.println("" + toStringJsonValue(iter.next()));
+    }
+    System.out.println("]");
+    System.out.println("*** End: Dumping List contents ***");
+  }
+
+  /*********************************************************************************
+   * void dumpList(List<JsonValue> list)
+   *********************************************************************************/
+  public static void dumpList(List<JsonValue> list) {
+    dumpList(list, null);
+  }
+
+  /*********************************************************************************
+   * String toStringList(List<JsonValue> list)
+   *********************************************************************************/
+  public static String toStringList(List<JsonValue> list) {
+    Iterator<JsonValue> iter = list.iterator();
+    StringBuilder sb = new StringBuilder();
+    sb.append("List: [");
+    String comma = "";
+    while (iter.hasNext()) {
+      sb.append(comma + toStringJsonValue(iter.next()));
+      if (comma.equals(""))
+        comma = ",";
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsList(List<JsonValue>expList, List<JsonValue>actList)
+   *********************************************************************************/
+  public static boolean assertEqualsList(List<JsonValue> expList,
+      List<JsonValue> actList) {
+    if (actList.equals(expList)) {
+      System.out.println("Lists are equal - match (Success)");
+      System.out.println("Expected: " + toStringList(expList));
+      System.out.println("Actual:   " + toStringList(actList));
+      return true;
+    } else {
+      System.out.println("Lists are not equal - mismatch (Failure)");
+      System.err.println("Expected: " + toStringList(expList));
+      System.err.println("Actual:   " + toStringList(actList));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * assertEqualsList2
+   *********************************************************************************/
+  public static boolean assertEqualsList2(List<JsonValue> expList,
+      List<JsonValue> actList) {
+    System.out.println(
+        "*** Comparing contents of List expList and List actList for equality ***");
+    System.out.println("Expected: " + toStringList(expList));
+    System.out.println("Actual:   " + toStringList(actList));
+    System.out.println("List expList size should equal List actList size");
+    if (expList.size() != actList.size()) {
+      System.err.println("List sizes are not equal: expList size " + expList.size()
+          + ", actList size " + actList.size());
+      return false;
+    }
+    System.out.println("Compare Lists (all elements should MATCH)");
+    for (int i = 0; i < expList.size(); i++) {
+      if (expList.get(i).equals(actList.get(i))) {
+        System.out.println("expList element " + i + " matches actList element " + i);
+      } else {
+        System.err.println(
+            "expList element " + i + " does not match actList element " + i);
+        System.err.println("expList[" + i + "]=" + expList.get(i));
+        System.err.println("actList[" + i + "]=" + actList.get(i));
+        return false;
+      }
+    }
+    System.out.println("Lists are equal (Success)");
+    return true;
+  }
+
+  /*********************************************************************************
+   * void dumpIterator(Iterator<JsonValue> iterator, String msg)
+   *********************************************************************************/
+  public static void dumpIterator(Iterator<JsonValue> iterator, String msg) {
+    System.out.println("*** Beg: Dumping Iterator contents ***");
+    if (msg != null)
+      System.out.println("*** Message: " + msg);
+    System.out.println("Iter: [");
+    while (iterator.hasNext()) {
+      System.out.println("" + toStringJsonValue(iterator.next()));
+    }
+    System.out.println("]");
+    System.out.println("*** End: Dumping Iterator contents ***");
+  }
+
+  /*********************************************************************************
+   * void dumpIterator(Iterator<JsonValue> iterator)
+   *********************************************************************************/
+  public static void dumpIterator(Iterator<JsonValue> iterator) {
+    dumpIterator(iterator, null);
+  }
+
+  /*********************************************************************************
+   * String toStringIterator(Iterator<JsonValue> iterator)
+   *********************************************************************************/
+  public static String toStringIterator(Iterator<JsonValue> iter) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("Iterator: [");
+    while (iter.hasNext()) {
+      sb.append(toStringJsonValue(iter.next()));
+      if (iter.hasNext())
+        sb.append(",");
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsIterator(Iterator<JsonValue>expIt,
+   * Iterator<JsonValue>actIt)
+   *********************************************************************************/
+  public static boolean assertEqualsIterator(Iterator<JsonValue> expIt,
+      Iterator<JsonValue> actIt) {
+    boolean pass = true;
+
+    System.out.println(
+        "*** Comparing contents of Iterator expIt and Iterator actIt for equality ***");
+    int i = 0;
+    while (expIt.hasNext()) {
+      if (!actIt.hasNext()) {
+        System.err.println(
+            "Iterator expIt contains more elements than Iterator actIt");
+        return false;
+      }
+      ++i;
+      JsonValue value1 = expIt.next();
+      JsonValue value2 = actIt.next();
+      if (assertEqualsJsonValues(value1, value2)) {
+        System.out.println("Iterator expIt element  " + i
+            + " matches Iterator actIt element " + i);
+      } else {
+        System.err.println("Iterator expIt element " + i
+            + " does not match Iterator actIt element " + i);
+        pass = false;
+      }
+    }
+    if (actIt.hasNext()) {
+      System.err.println("Iterator actIt contains more elements than Iterator expIt");
+      return false;
+    }
+    if (pass)
+      System.out.println("Iterators are equal (Success)");
+    else
+      System.out.println("Iterators are not equal (Failure)");
+    return pass;
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsEmptyArrayList(List<JsonValue> actual)
+   *********************************************************************************/
+  public static boolean assertEqualsEmptyArrayList(List<JsonValue> actual) {
+    if (actual.isEmpty()) {
+      System.out.println("Array List is empty - expected");
+      return true;
+    } else {
+      System.err.println("Array List is not empty - unexpected");
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsEmptyObjectMap(Map<String, JsonValue> actual)
+   *********************************************************************************/
+  public static boolean assertEqualsEmptyObjectMap(
+      Map<String, JsonValue> actual) {
+    if (actual.isEmpty()) {
+      System.out.println("Object Map is empty - expected");
+      return true;
+    } else {
+      System.err.println("Object Map is not empty - unexpected");
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsEmptyIterator(Map<String, JsonValue> actual)
+   *********************************************************************************/
+  public static boolean assertEqualsEmptyIterator(Iterator<JsonValue> actual) {
+    if (!actual.hasNext()) {
+      System.out.println("Iterator is empty - expected");
+      return true;
+    } else {
+      System.err.println("Iterator is not empty - unexpected");
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonText(String expected, String actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonText(String expected, String actual) {
+    if (actual.equals(expected)) {
+      System.out.println("JSON text match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("JSON text mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonArrays(JsonArray expected, JsonArray actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonArrays(JsonArray expected,
+      JsonArray actual) {
+    if (actual.equals(expected)) {
+      System.out.println("JsonArray match");
+      System.out.println("Expected: " + toStringJsonArray(expected));
+      System.out.println("Actual:   " + toStringJsonArray(actual));
+      return true;
+    } else {
+      System.err.println("JsonArray mismatch");
+      System.err.println("Expected: " + toStringJsonArray(expected));
+      System.err.println("Actual:   " + toStringJsonArray(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonObjects(JsonObject expected, JsonObject actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonObjects(JsonObject expected,
+      JsonObject actual) {
+    if (actual.equals(expected)) {
+      System.out.println("JsonObject match");
+      System.out.println("Expected: " + toStringJsonObject(expected));
+      System.out.println("Actual:   " + toStringJsonObject(actual));
+      return true;
+    } else {
+      System.err.println("JsonObject mismatch");
+      System.err.println("Expected: " + toStringJsonObject(expected));
+      System.err.println("Actual:   " + toStringJsonObject(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonNumbers(JsonNumber expected, JsonNumber actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonNumbers(JsonNumber expected,
+      JsonNumber actual) {
+    boolean pass = true;
+
+    if (actual.equals(expected)) {
+      System.out.println("JsonNumber match");
+      System.out.println("Expected: " + toStringJsonNumber(expected));
+      System.out.println("Actual:   " + toStringJsonNumber(actual));
+      return true;
+    } else {
+      System.err.println("JsonNumber mismatch");
+      System.err.println("Expected: " + toStringJsonNumber(expected));
+      System.err.println("Actual:   " + toStringJsonNumber(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonStrings(JsonString expected, JsonString actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonStrings(JsonString expected,
+      JsonString actual) {
+    boolean pass = true;
+
+    if (actual.equals(expected)) {
+      System.out.println("JsonString match");
+      System.out.println("Expected: " + toStringJsonString(expected));
+      System.out.println("Actual:   " + toStringJsonString(actual));
+      return true;
+    } else {
+      System.err.println("JsonString mismatch");
+      System.err.println("Expected: " + toStringJsonString(expected));
+      System.err.println("Actual:   " + toStringJsonString(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonValues(JsonValue expected, JsonValue actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonValues(JsonValue expected,
+      JsonValue actual) {
+    boolean pass = true;
+
+    // Comparing JsonNumbers
+    if (expected instanceof JsonNumber) {
+      if (!(actual instanceof JsonNumber)) {
+        System.err.println("expected type does not match actual type");
+        System.err.println("expected=" + toStringJsonValue(expected));
+        System.err.println("actual=  " + toStringJsonValue(actual));
+        pass = false;
+      } else {
+        pass = assertEqualsJsonNumbers((JsonNumber) expected,
+            (JsonNumber) actual);
+      }
+      // Comparing JsonStrings
+    } else if (expected instanceof JsonString) {
+      if (!(actual instanceof JsonString)) {
+        System.err.println("expected type does not match actual type");
+        System.err.println("expected=" + toStringJsonValue(expected));
+        System.err.println("actual=  " + toStringJsonValue(actual));
+        pass = false;
+      } else {
+        pass = assertEqualsJsonStrings((JsonString) expected,
+            (JsonString) actual);
+      }
+      // Comparing JsonArrays
+    } else if (expected instanceof JsonArray) {
+      if (!(actual instanceof JsonArray)) {
+        System.err.println("expected type does not match actual type");
+        System.err.println("expected=" + toStringJsonValue(expected));
+        System.err.println("actual=  " + toStringJsonValue(actual));
+        pass = false;
+      } else {
+        pass = assertEqualsJsonArrays((JsonArray) expected, (JsonArray) actual);
+      }
+      // Comparing JsonObjects
+    } else if (expected instanceof JsonObject) {
+      if (!(actual instanceof JsonObject)) {
+        System.err.println("expected type does not match actual type");
+        System.err.println("expected=" + toStringJsonValue(expected));
+        System.err.println("actual=  " + toStringJsonValue(actual));
+        pass = false;
+      } else {
+        pass = assertEqualsJsonObjects((JsonObject) expected,
+            (JsonObject) actual);
+      }
+      // Comparing JsonValues
+    } else if (expected.equals(actual)) {
+      System.out.println("expected matches actual");
+      System.out.println("expected=" + toStringJsonValue(expected));
+      System.out.println("actual=  " + toStringJsonValue(actual));
+    } else {
+      System.err.println("expected does not match actual");
+      System.err.println("expected=" + toStringJsonValue(expected));
+      System.err.println("actual=  " + toStringJsonValue(actual));
+      pass = false;
+    }
+    return pass;
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonValueType(JsonValue.ValueType
+   * expected,JsonValue.ValueType actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonValueType(JsonValue.ValueType expected,
+      JsonValue.ValueType actual) {
+    if (actual == expected) {
+      System.out.println("JsonValue.ValueType match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("JsonValue.ValueType mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonNumberType(boolean expected,boolean actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonNumberType(boolean expected,
+      boolean actual) {
+    if (actual == expected) {
+      System.out.println("Json NumberType match");
+      System.out.println("Expected: " + toStringJsonNumberType(expected));
+      System.out.println("Actual:   " + toStringJsonNumberType(actual));
+      return true;
+    } else {
+      System.err.println("Json NumberType mismatch");
+      System.err.println("Expected: " + toStringJsonNumberType(expected));
+      System.err.println("Actual:   " + toStringJsonNumberType(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEqualsJsonNumberTypes(boolean expected[],boolean actual)
+   *********************************************************************************/
+  public static boolean assertEqualsJsonNumberTypes(boolean expected[],
+      boolean actual) {
+    for (int i = 0; i < expected.length; i++) {
+      if (actual == expected[i]) {
+        System.out.println("Json NumberType match");
+        System.out.println("Expected: " + toStringJsonNumberType(expected[i]));
+        System.out.println("Actual:   " + toStringJsonNumberType(actual));
+        return true;
+      }
+    }
+    System.err.println("Json NumberType mismatch");
+    System.err.println("Expected: " + toStringJsonNumberTypes(expected));
+    System.err.println("Actual:   " + toStringJsonNumberType(actual));
+    return false;
+  }
+
+  /*********************************************************************************
+   * String toStringJsonNumberType(boolean numberType)
+   *********************************************************************************/
+  public static String toStringJsonNumberType(boolean numberType) {
+    if (numberType == INTEGRAL)
+      return "INTEGRAL";
+    else
+      return "NON_INTEGRAL";
+  }
+
+  /*********************************************************************************
+   * String toStringJsonNumberTypes(boolean expected[])
+   *********************************************************************************/
+  public static String toStringJsonNumberTypes(boolean expected[]) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < expected.length; i++) {
+      sb.append("" + toStringJsonNumberType(expected[i]));
+      if (i + 1 < expected.length)
+        sb.append("|");
+    }
+    return sb.toString();
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(Object, Object)
+   *********************************************************************************/
+  public static boolean assertEquals(Object expected, Object actual) {
+    if (actual.equals(expected)) {
+      System.out.println("Object match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("Object mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(boolean, boolean)
+   *********************************************************************************/
+  public static boolean assertEquals(boolean expected, boolean actual) {
+    if (actual == expected) {
+      System.out.println("boolean match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("boolean mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(short, short)
+   *********************************************************************************/
+  public static boolean assertEquals(short expected, short actual) {
+    if (actual == expected) {
+      System.out.println("short match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("short mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(int, int)
+   *********************************************************************************/
+  public static boolean assertEquals(int expected, int actual) {
+    if (actual == expected) {
+      System.out.println("int match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("int mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(long, long)
+   *********************************************************************************/
+  public static boolean assertEquals(long expected, long actual) {
+    if (actual == expected) {
+      System.out.println("long match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("long mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(float, float)
+   *********************************************************************************/
+  public static boolean assertEquals(float expected, float actual) {
+    if (actual == expected) {
+      System.out.println("float match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("float mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(double, double)
+   *********************************************************************************/
+  public static boolean assertEquals(double expected, double actual) {
+    if (actual == expected) {
+      System.out.println("double match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("double mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(BigDecimal, BigDecimal)
+   *********************************************************************************/
+  public static boolean assertEquals(BigDecimal expected, BigDecimal actual) {
+    if (actual.equals(expected)) {
+      System.out.println("BigDecimal match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("BigDecimal mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(BigInteger, BigInteger)
+   *********************************************************************************/
+  public static boolean assertEquals(BigInteger expected, BigInteger actual) {
+    if (actual.equals(expected)) {
+      System.out.println("BigInteger match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("BigInteger mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(String, String)
+   *********************************************************************************/
+  public static boolean assertEquals(String expected, String actual) {
+    if (actual.equals(expected)) {
+      System.out.println("String match");
+      System.out.println("Expected: " + expected);
+      System.out.println("Actual:   " + actual);
+      return true;
+    } else {
+      System.err.println("String mismatch");
+      System.err.println("Expected: " + expected);
+      System.err.println("Actual:   " + actual);
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(JsonValue, JsonValue)
+   *********************************************************************************/
+  public static boolean assertEquals(JsonValue expected, JsonValue actual) {
+    if (actual.equals(expected)) {
+      System.out.println("JsonValue match");
+      System.out.println("Expected: " + toStringJsonValue(expected));
+      System.out.println("Actual:   " + toStringJsonValue(actual));
+      return true;
+    } else {
+      System.err.println("JsonValue mismatch");
+      System.err.println("Expected: " + toStringJsonValue(expected));
+      System.err.println("Actual:   " + toStringJsonValue(actual));
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * String getNumberTypeString(boolean numberType)
+   *********************************************************************************/
+  public static String getNumberTypeString(boolean numberType) {
+    if (numberType == INTEGRAL)
+      return "INTEGRAL";
+    else
+      return "NON_INTEGRAL";
+  }
+
+  /*********************************************************************************
+   * boolean getNumberType(String numberType)
+   *********************************************************************************/
+  public static boolean getNumberType(String numberType) {
+    if (numberType.equals("INTEGRAL"))
+      return INTEGRAL;
+    else
+      return NON_INTEGRAL;
+  }
+
+  /*********************************************************************************
+   * String getValueTypeString(JsonValue.ValueType valueType)
+   *********************************************************************************/
+  public static String getValueTypeString(JsonValue.ValueType valueType) {
+    switch (valueType) {
+    case ARRAY:
+      return "ARRAY";
+    case FALSE:
+      return "FALSE";
+    case NULL:
+      return "NULL";
+    case NUMBER:
+      return "NUMBER";
+    case OBJECT:
+      return "OBJECT";
+    case STRING:
+      return "STRING";
+    case TRUE:
+      return "TRUE";
+    default:
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * JsonValue.ValueType getValueType(String valueType)
+   *********************************************************************************/
+  public static JsonValue.ValueType getValueType(String valueType) {
+    if (valueType.equals("ARRAY"))
+      return JsonValue.ValueType.ARRAY;
+    if (valueType.equals("FALSE"))
+      return JsonValue.ValueType.FALSE;
+    if (valueType.equals("NULL"))
+      return JsonValue.ValueType.NULL;
+    if (valueType.equals("NUMBER"))
+      return JsonValue.ValueType.NUMBER;
+    if (valueType.equals("OBJECT"))
+      return JsonValue.ValueType.OBJECT;
+    if (valueType.equals("STRING"))
+      return JsonValue.ValueType.STRING;
+    if (valueType.equals("TRUE"))
+      return JsonValue.ValueType.TRUE;
+    else
+      return null;
+  }
+
+  /*********************************************************************************
+   * void dumpEventType(JsonParser.Event eventType)
+   *********************************************************************************/
+  public static void dumpEventType(JsonParser.Event eventType) {
+    System.out.println("JsonParser.Event=" + eventType);
+  }
+
+  /*********************************************************************************
+   * getEventTypeString(JsonParser.Event eventType)
+   *********************************************************************************/
+  public static String getEventTypeString(JsonParser.Event eventType) {
+    switch (eventType) {
+    case START_ARRAY:
+      return "START_ARRAY";
+    case START_OBJECT:
+      return "START_OBJECT";
+    case KEY_NAME:
+      return "KEY_NAME";
+    case VALUE_STRING:
+      return "VALUE_STRING";
+    case VALUE_NUMBER:
+      return "VALUE_NUMBER";
+    case VALUE_TRUE:
+      return "VALUE_TRUE";
+    case VALUE_FALSE:
+      return "VALUE_FALSE";
+    case VALUE_NULL:
+      return "VALUE_NULL";
+    case END_OBJECT:
+      return "END_OBJECT";
+    case END_ARRAY:
+      return "END_ARRAY";
+    default:
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * JsonParser.Event getEventType(String eventType)
+   *********************************************************************************/
+  public static JsonParser.Event getEventType(String eventType) {
+    if (eventType.equals("START_ARRAY"))
+      return JsonParser.Event.START_ARRAY;
+    if (eventType.equals("START_OBJECT"))
+      return JsonParser.Event.START_OBJECT;
+    if (eventType.equals("KEY_NAME"))
+      return JsonParser.Event.KEY_NAME;
+    if (eventType.equals("VALUE_STRING"))
+      return JsonParser.Event.VALUE_STRING;
+    if (eventType.equals("VALUE_NUMBER"))
+      return JsonParser.Event.VALUE_NUMBER;
+    if (eventType.equals("VALUE_TRUE"))
+      return JsonParser.Event.VALUE_TRUE;
+    if (eventType.equals("VALUE_FALSE"))
+      return JsonParser.Event.VALUE_FALSE;
+    if (eventType.equals("VALUE_NULL"))
+      return JsonParser.Event.VALUE_NULL;
+    if (eventType.equals("END_OBJECT"))
+      return JsonParser.Event.END_OBJECT;
+    if (eventType.equals("END_ARRAY"))
+      return JsonParser.Event.END_ARRAY;
+    else
+      return null;
+  }
+
+  /*********************************************************************************
+   * String getConfigName(String configValue)
+   *********************************************************************************/
+  public static String getConfigName(String configValue) {
+    if (configValue.equals(JsonGenerator.PRETTY_PRINTING))
+      return "JsonGenerator.PRETTY_PRINTING";
+    else if (configValue.equals(JSONP_Util.FOO_CONFIG))
+      return "JSONP_Util.FOO_CONFIG";
+    else
+      return null;
+  }
+
+  /*********************************************************************************
+   * String getConfigValue(String configProp)
+   *********************************************************************************/
+  public static String getConfigValue(String configProp) {
+    if (configProp.equals("JsonGenerator.PRETTY_PRINING"))
+      return JsonGenerator.PRETTY_PRINTING;
+    else if (configProp.equals("JSONP_Util.FOO_CONFIG"))
+      return JSONP_Util.FOO_CONFIG;
+    else
+      return null;
+  }
+
+  /*********************************************************************************
+   * void dumpConfigMap(Map<String,?> map, String msg)
+   *********************************************************************************/
+  public static void dumpConfigMap(Map<String, ?> map, String msg) {
+    System.out.println("*** Beg: Dumping Config Map contents ***");
+    if (msg != null)
+      System.out.println("*** Message: " + msg);
+    for (Map.Entry<String, ?> entry : map.entrySet()) {
+      System.out.println("\"" + entry.getKey() + "\":" + entry.getValue());
+    }
+    System.out.println("*** End: Dumping Config Map contents ***");
+  }
+
+  /*********************************************************************************
+   * void dumpConfigMap(Map<String,?> map)
+   *********************************************************************************/
+  public static void dumpConfigMap(Map<String, ?> map) {
+    dumpConfigMap(map, null);
+  }
+
+  /*********************************************************************************
+   * boolean doConfigCheck(Map<String,?> config, int expectedSize)
+   *********************************************************************************/
+  public static boolean doConfigCheck(Map<String, ?> config, int expectedSize) {
+    return doConfigCheck(config, expectedSize, null);
+  }
+
+  public static boolean doConfigCheck(Map<String, ?> config, int expectedSize,
+      String[] expectedProps) {
+    boolean pass = true;
+    dumpConfigMap(config);
+    System.out.println("Checking factory configuration property size");
+    if (config.size() != expectedSize) {
+      System.err.println("Expecting no of properties=" + expectedSize + ", got="
+          + config.size());
+      pass = false;
+    } else {
+      System.out.println("Expecting no of properties=" + expectedSize + ", got="
+          + config.size());
+    }
+    if (expectedSize != 0 && expectedProps != null) {
+      System.out.println("Checking factory configuration property name and value");
+      for (int i = 0; i < expectedProps.length; i++) {
+        if (config.containsKey(expectedProps[i])) {
+          System.out.println("Does contain key: " + expectedProps[i] + " - expected.");
+          if (!JSONP_Util.assertEquals(true, config.get(expectedProps[i]))) {
+            pass = false;
+          }
+        } else {
+          System.err.println(
+              "Does not contain key: " + expectedProps[i] + " - unexpected.");
+          pass = false;
+        }
+      }
+    }
+    return pass;
+  }
+
+  /*********************************************************************************
+   * boolean isEmptyConfig(Map<String, ?> config)
+   *********************************************************************************/
+  public boolean isEmptyConfig(Map<String, ?> config) {
+    System.out.println("isEmptyConfig");
+    return config.isEmpty();
+  }
+
+  /*********************************************************************************
+   * Map<String, ?> getEmptyConfig()
+   *********************************************************************************/
+  public static Map<String, ?> getEmptyConfig() {
+    System.out.println("getEmptyConfig");
+    Map<String, Object> config = new HashMap<String, Object>();
+    return config;
+  }
+
+  /*********************************************************************************
+   * Map<String, ?> getPrettyPrintingConfig()
+   *********************************************************************************/
+  public static Map<String, ?> getPrettyPrintingConfig() {
+    System.out.println("getPrettyPrintConfig");
+    Map<String, Object> config = new HashMap<String, Object>();
+    System.out.println("Added property: JsonGenerator.PRETTY_PRINTING");
+    config.put(JsonGenerator.PRETTY_PRINTING, true);
+    return config;
+  }
+
+  /*********************************************************************************
+   * Map<String, ?> getFooConfig()
+   *********************************************************************************/
+  public static Map<String, ?> getFooConfig() {
+    System.out.println("getFooConfig");
+    Map<String, Object> config = new HashMap<String, Object>();
+    System.out.println("Added property: JSONP_Util.FOO_CONFIG");
+    config.put(JSONP_Util.FOO_CONFIG, true);
+    return config;
+  }
+
+  /*********************************************************************************
+   * Map<String, ?> getAllConfig()
+   *********************************************************************************/
+  public static Map<String, ?> getAllConfig() {
+    System.out.println("getAllConfig");
+    Map<String, Object> config = new HashMap<String, Object>();
+    System.out.println("Added property: JsonGenerator.PRETTY_PRINTING");
+    config.put(JsonGenerator.PRETTY_PRINTING, true);
+    System.out.println("Added property: JSONP_Util.FOO_CONFIG");
+    config.put(JSONP_Util.FOO_CONFIG, true);
+    return config;
+  }
+
+  /*********************************************************************************
+   * JsonObject createJsonObjectFromString(String jsonObjData)
+   *********************************************************************************/
+  public static JsonObject createJsonObjectFromString(String jsonObjData) {
+    JsonReader reader = null;
+    JsonObject object = null;
+    try {
+      reader = Json.createReader(new StringReader(jsonObjData));
+      object = reader.readObject();
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    return object;
+  }
+
+  /*********************************************************************************
+   * JsonArray createJsonArrayFromString(String jsonArrData)
+   *********************************************************************************/
+  public static JsonArray createJsonArrayFromString(String jsonArrData) {
+    JsonReader reader = null;
+    JsonArray array = null;
+    try {
+      reader = Json.createReader(new StringReader(jsonArrData));
+      array = reader.readArray();
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    return array;
+  }
+
+  /*********************************************************************************
+   * void writeJsonObjectFromString(JsonWriter writer, String jsonObjData)
+   *********************************************************************************/
+  public static void writeJsonObjectFromString(JsonWriter writer,
+      String jsonObjData) {
+    try {
+      JsonObject jsonObject = createJsonObjectFromString(jsonObjData);
+      writer.writeObject(jsonObject);
+      writer.close();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+    }
+  }
+
+  /*********************************************************************************
+   * void writeJsonArrayFromString(JsonWriter writer, String jsonArrData)
+   *********************************************************************************/
+  public static void writeJsonArrayFromString(JsonWriter writer,
+      String jsonArrData) {
+    try {
+      JsonArray jsonArray = createJsonArrayFromString(jsonArrData);
+      writer.writeArray(jsonArray);
+      writer.close();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyStringValue(JsonParser parser, String name, String value)
+   *********************************************************************************/
+  public static void testKeyStringValue(JsonParser parser, String name,
+      String value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_STRING) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_STRING)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    }
+    String keyvalue = parser.getString();
+    if (!keyvalue.equals(value)) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyIntegerValue(JsonParser parser, String name, int value)
+   *********************************************************************************/
+  public static void testKeyIntegerValue(JsonParser parser, String name,
+      int value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    int keyvalue = parser.getInt();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyDoubleValue(JsonParser parser, String name, double value)
+   *********************************************************************************/
+  public static void testKeyDoubleValue(JsonParser parser, String name,
+      double value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    double keyvalue = parser.getBigDecimal().doubleValue();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyLongValue(JsonParser parser, String name, long value)
+   *********************************************************************************/
+  public static void testKeyLongValue(JsonParser parser, String name,
+      long value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    long keyvalue = parser.getLong();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyBigDecimalValue(JsonParser parser, String name, BigDecimal
+   * value)
+   *********************************************************************************/
+  public static void testKeyBigDecimalValue(JsonParser parser, String name,
+      BigDecimal value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    BigDecimal keyvalue = parser.getBigDecimal();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyTrueValue(JsonParser parser, String name)
+   *********************************************************************************/
+  public static void testKeyTrueValue(JsonParser parser, String name) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_TRUE) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_TRUE)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyFalseValue(JsonParser parser, String name)
+   *********************************************************************************/
+  public static void testKeyFalseValue(JsonParser parser, String name) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_FALSE) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_FALSE)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyNullValue(JsonParser parser, String name)
+   *********************************************************************************/
+  public static void testKeyNullValue(JsonParser parser, String name) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.VALUE_NULL) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NULL)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyStartObjectValue(JsonParser parser, String name)
+   *********************************************************************************/
+  public static void testKeyStartObjectValue(JsonParser parser, String name) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.START_OBJECT) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.START_OBJECT)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * void testKeyStartArrayValue(JsonParser parser, String name)
+   *********************************************************************************/
+  public static void testKeyStartArrayValue(JsonParser parser, String name) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+
+    if (e != JsonParser.Event.KEY_NAME) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.KEY_NAME)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyname = parser.getString();
+    if (!name.equals(keyname)) {
+      System.err.println("Expected keyname: " + name + ", got keyname: " + keyname);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyname: " + keyname);
+    }
+
+    if (!checkNextParserEvent(parser))
+      return;
+    e = parser.next();
+    if (e != JsonParser.Event.START_ARRAY) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.START_ARRAY)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * boolean checkNextParserEvent(JsonParser parser)
+   *********************************************************************************/
+  public static boolean checkNextParserEvent(JsonParser parser) {
+    if (!parser.hasNext()) {
+      System.err.println("no next parser event found - unexpected");
+      parseErrs++;
+      return false;
+    } else
+      return true;
+  }
+
+  /*********************************************************************************
+   * JsonParser.Event getNextParserEvent(JsonParser parser)
+   *********************************************************************************/
+  public static JsonParser.Event getNextParserEvent(JsonParser parser) {
+    if (parser.hasNext())
+      return parser.next();
+    else
+      return null;
+  }
+
+  /*********************************************************************************
+   * JsonParser.Event getNextSpecificParserEvent(JsonParser parser,
+   * JsonParser.Event thisEvent)
+   *********************************************************************************/
+  public static JsonParser.Event getNextSpecificParserEvent(JsonParser parser,
+      JsonParser.Event thisEvent) {
+    while (parser.hasNext()) {
+      JsonParser.Event event = parser.next();
+      if (event == thisEvent)
+        return event;
+    }
+    return null;
+  }
+
+  /*********************************************************************************
+   * void testEventType(JsonParser parser, JsonParser.Event expEvent)
+   *********************************************************************************/
+  public static void testEventType(JsonParser parser,
+      JsonParser.Event expEvent) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != expEvent) {
+      System.err.println("Expected event: " + getEventTypeString(expEvent)
+          + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+  }
+
+  /*********************************************************************************
+   * void testStringValue(JsonParser parser, String value)
+   *********************************************************************************/
+  public static void testStringValue(JsonParser parser, String value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != JsonParser.Event.VALUE_STRING) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_STRING)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    String keyvalue = parser.getString();
+    if (!keyvalue.equals(value)) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testIntegerValue(JsonParser parser, int value)
+   *********************************************************************************/
+  public static void testIntegerValue(JsonParser parser, int value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    int keyvalue = parser.getInt();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testDoubleValue(JsonParser parser, double value)
+   *********************************************************************************/
+  public static void testDoubleValue(JsonParser parser, double value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    double keyvalue = parser.getBigDecimal().doubleValue();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testLongValue(JsonParser parser, long value)
+   *********************************************************************************/
+  public static void testLongValue(JsonParser parser, long value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    long keyvalue = parser.getLong();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testBigDecimalValue(JsonParser parser, BigDecimal value)
+   *********************************************************************************/
+  public static void testBigDecimalValue(JsonParser parser, BigDecimal value) {
+    if (!checkNextParserEvent(parser))
+      return;
+    JsonParser.Event e = parser.next();
+    if (e != JsonParser.Event.VALUE_NUMBER) {
+      System.err.println(
+          "Expected event: " + getEventTypeString(JsonParser.Event.VALUE_NUMBER)
+              + ", got event: " + getEventTypeString(e));
+      parseErrs++;
+    } else {
+      System.out.println("Got expected event: " + getEventTypeString(e));
+    }
+    BigDecimal keyvalue = parser.getBigDecimal();
+    if (keyvalue != value) {
+      System.err.println(
+          "Expected keyvalue: " + value + ", got keyvalue: " + keyvalue);
+      parseErrs++;
+    } else {
+      System.out.println("Got expected keyvalue: " + keyvalue);
+    }
+  }
+
+  /*********************************************************************************
+   * void testTrueValue(JsonParser parser, JsonParser.Event expEvent)
+   *********************************************************************************/
+  public static void testTrueValue(JsonParser parser,
+      JsonParser.Event expEvent) {
+    testEventType(parser, expEvent);
+  }
+
+  /*********************************************************************************
+   * void testFalseValue(JsonParser parser, JsonParser.Event expEvent)
+   *********************************************************************************/
+  public static void testFalseValue(JsonParser parser,
+      JsonParser.Event expEvent) {
+    testEventType(parser, expEvent);
+  }
+
+  /*********************************************************************************
+   * void testNullValue(JsonParser parser, JsonParser.Event expEvent)
+   *********************************************************************************/
+  public static void testNullValue(JsonParser parser,
+      JsonParser.Event expEvent) {
+    testEventType(parser, expEvent);
+  }
+
+  /*********************************************************************************
+   * resetParseErrs()
+   *********************************************************************************/
+  public static void resetParseErrs() {
+    parseErrs = 0;
+  }
+
+  /*********************************************************************************
+   * int getParseErrs()
+   *********************************************************************************/
+  public static int getParseErrs() {
+    return parseErrs;
+  }
+
+  /*********************************************************************************
+   * String convertUnicodeCharToString(Char c)
+   *
+   * Convert unicode to string value of form U+NNNN where NNNN are 4 hex digits
+   *
+   * Use a binary or with hex value '0x10000' when converting unicode char to
+   * hex string and remove 1st char to get the 4 hex digits we need.
+   *********************************************************************************/
+  public static String convertUnicodeCharToString(char c) {
+    return "\\u" + Integer.toHexString((int) c | 0x10000).substring(1);
+  }
+
+  /*********************************************************************************
+   * boolean isUnicodeControlChar(Char c)
+   *
+   * The following unicode control chars:
+   *
+   * U+0000 - U+001F and U+007F U+0080 - U+009F
+   *********************************************************************************/
+  public static boolean isUnicodeControlChar(char c) {
+
+    if ((c >= '\u0000' && c <= '\u001F') || (c == '\u007F')
+        || (c >= '\u0080' && c <= '\u009F'))
+      return true;
+    else
+      return false;
+  }
+
+  /*********************************************************************************
+   * void writeStringToFile(String string, String file, String encoding)
+   *********************************************************************************/
+  public static void writeStringToFile(String string, String file,
+      String encoding) {
+    try {
+      FileOutputStream fos = new FileOutputStream(file);
+      Writer out = new OutputStreamWriter(fos, encoding);
+      out.write(string);
+      out.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /*********************************************************************************
+   * String readStringFromFile(String file, String encoding)
+   *********************************************************************************/
+  public static String readStringFromFile(String file, String encoding) {
+    StringBuffer buffer = new StringBuffer();
+    try {
+      FileInputStream fis = new FileInputStream(file);
+      InputStreamReader isr = new InputStreamReader(fis, encoding);
+      Reader in = new BufferedReader(isr);
+      int ch;
+      while ((ch = in.read()) > -1) {
+        buffer.append((char) ch);
+      }
+      in.close();
+      return buffer.toString();
+    } catch (IOException e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * void writeStringToStream(String string, OutputStream os, String encoding)
+   *********************************************************************************/
+  public static void writeStringToStream(String string, OutputStream os,
+      String encoding) {
+    try {
+      Writer out = new OutputStreamWriter(os, encoding);
+      out.write(string);
+      out.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /*********************************************************************************
+   * String readStringFromStream(InputStream is, String encoding)
+   *********************************************************************************/
+  public static String readStringFromStream(InputStream is, String encoding) {
+    StringBuffer buffer = new StringBuffer();
+    try {
+      InputStreamReader isr = new InputStreamReader(is, encoding);
+      Reader in = new BufferedReader(isr);
+      int ch;
+      while ((ch = in.read()) > -1) {
+        buffer.append((char) ch);
+      }
+      in.close();
+      return buffer.toString();
+    } catch (IOException e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * void writeStringToFile(String string, String file, Charset charset)
+   *********************************************************************************/
+  public static void writeStringToFile(String string, String file,
+      Charset charset) {
+    try {
+      FileOutputStream fos = new FileOutputStream(file);
+      Writer out = new OutputStreamWriter(fos, charset);
+      out.write(string);
+      out.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /*********************************************************************************
+   * String readStringFromFile(String file, Charset charset)
+   *********************************************************************************/
+  public static String readStringFromFile(String file, Charset charset) {
+    StringBuffer buffer = new StringBuffer();
+    try {
+      FileInputStream fis = new FileInputStream(file);
+      InputStreamReader isr = new InputStreamReader(fis, charset);
+      Reader in = new BufferedReader(isr);
+      int ch;
+      while ((ch = in.read()) > -1) {
+        buffer.append((char) ch);
+      }
+      in.close();
+      return buffer.toString();
+    } catch (IOException e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * void writeStringToStream(String string, OutputStream os, Charset charset)
+   *********************************************************************************/
+  public static void writeStringToStream(String string, OutputStream os,
+      Charset charset) {
+    try {
+      Writer out = new OutputStreamWriter(os, charset);
+      out.write(string);
+      out.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /*********************************************************************************
+   * String readStringFromStream(InputStream is, Charset charset)
+   *********************************************************************************/
+  public static String readStringFromStream(InputStream is, Charset charset) {
+    StringBuffer buffer = new StringBuffer();
+    try {
+      InputStreamReader isr = new InputStreamReader(is, charset);
+      Reader in = new BufferedReader(isr);
+      int ch;
+      while ((ch = in.read()) > -1) {
+        buffer.append((char) ch);
+      }
+      in.close();
+      return buffer.toString();
+    } catch (IOException e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * Charset getCharset(String encoding)
+   *********************************************************************************/
+  public static Charset getCharset(String encoding) {
+    Charset cs = null;
+
+    try {
+      cs = Charset.forName(encoding);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return cs;
+  }
+
+  /*********************************************************************************
+   * void dumpLocation(JsonLocation location)
+   *********************************************************************************/
+  public static void dumpLocation(JsonLocation location) {
+    if (location != null) {
+      System.out.println("JsonLocation info: lineNumber=" + location.getLineNumber()
+              + ", columnNumber=" + location.getColumnNumber()
+              + ", streamOffset=" + location.getStreamOffset());
+    } else {
+      System.out.println("JsonLocation is null - no location info");
+    }
+  }
+
+  /*********************************************************************************
+   * void dumpLocation(JsonParser parser)
+   *********************************************************************************/
+  public static void dumpLocation(JsonParser parser) {
+    dumpLocation(parser.getLocation());
+  }
+
+  /*********************************************************************************
+   * boolean assertEquals(JsonLocation, JsonLocation)
+   *********************************************************************************/
+  public static boolean assertEquals(JsonLocation expLoc, JsonLocation actLoc) {
+    if (expLoc.getLineNumber() == actLoc.getLineNumber()
+        && expLoc.getColumnNumber() == actLoc.getColumnNumber()
+        && expLoc.getStreamOffset() == actLoc.getStreamOffset()) {
+      System.out.println("JsonLocations equal - match (Success)");
+      System.out.println(
+          "Expected: JsonLocation info: lineNumber=" + expLoc.getLineNumber()
+              + ", columnNumber=" + expLoc.getColumnNumber() + ", streamOffset="
+              + expLoc.getStreamOffset());
+      System.out.println(
+          "Actual:   JsonLocation info: lineNumber=" + actLoc.getLineNumber()
+              + ", columnNumber=" + actLoc.getColumnNumber() + ", streamOffset="
+              + actLoc.getStreamOffset());
+      return true;
+    } else {
+      System.err.println("JsonLocations not equal - mismatch (Failure)");
+      System.err.println(
+          "Expected: JsonLocation info: lineNumber=" + expLoc.getLineNumber()
+              + ", columnNumber=" + expLoc.getColumnNumber() + ", streamOffset="
+              + expLoc.getStreamOffset());
+      System.err.println(
+          "Actual:   JsonLocation info: lineNumber=" + actLoc.getLineNumber()
+              + ", columnNumber=" + actLoc.getColumnNumber() + ", streamOffset="
+              + actLoc.getStreamOffset());
+      return false;
+    }
+  }
+
+  /*********************************************************************************
+   * void addFileToClassPath(String s)
+   *********************************************************************************/
+  public static void addFileToClassPath(String s) throws Exception {
+    addFileToClassPath(new File(s));
+  }
+
+  /*********************************************************************************
+   * void addFileToClassPath(File f)
+   *********************************************************************************/
+  public static void addFileToClassPath(File f) throws Exception {
+    addURLToClassPath((f.toURI()).toURL());
+  }
+
+  /*********************************************************************************
+   * void addURLToClassPath(URL url)
+   *********************************************************************************/
+  public static void addURLToClassPath(URL url) throws Exception {
+    System.out.println("addURLToClassPath-> " + url.toString());
+    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader
+        .getSystemClassLoader();
+    try {
+      Class urlClassLoaderClass = URLClassLoader.class;
+      Method method = urlClassLoaderClass.getDeclaredMethod("addURL",
+          new Class[] { URL.class });
+      method.setAccessible(true);
+      method.invoke(urlClassLoader, new Object[] { url });
+    } catch (Throwable t) {
+      t.printStackTrace();
+      throw new IOException("Error, could not add URL to system classloader");
+    }
+  }
+
+  /*********************************************************************************
+   * JsonArray buildJsonArrayFooBar
+   *********************************************************************************/
+  public static JsonArray buildJsonArrayFooBar() {
+    try {
+      JsonArray jsonArray = Json.createArrayBuilder().add("foo").add("bar")
+          .build();
+      return jsonArray;
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  public static final String JSONARRAYFOOBAR = "[\"foo\",\"bar\"]";
+
+  /*********************************************************************************
+   * JsonObject buildJsonObjectFooBar()
+   *********************************************************************************/
+  public static JsonObject buildJsonObjectFooBar() {
+    try {
+      JsonObject jsonObject = Json.createObjectBuilder().add("foo", "bar")
+          .build();
+      return jsonObject;
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  public static final String JSONOBJECTFOOBAR = "{\"foo\":\"bar\"}";
+
+  /*********************************************************************************
+   * JsonObject createSampleJsonObject()
+   *
+   * Assertion ids covered: 400/401/403/404/406/408/409
+   *********************************************************************************/
+  public static JsonObject createSampleJsonObject() throws Exception {
+    JsonObject object = Json.createObjectBuilder().add("firstName", "John")
+        .add("lastName", "Smith").add("age", 25).add("elderly", JsonValue.FALSE)
+        .add("patriot", JsonValue.TRUE)
+        .add("address",
+            Json.createObjectBuilder().add("streetAddress", "21 2nd Street")
+                .add("city", "New York").add("state", "NY")
+                .add("postalCode", "10021"))
+        .add("phoneNumber",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(Json.createObjectBuilder().add("type", "cell")
+                    .add("number", "646 555-4567")))
+        .add("objectOfFooBar",
+            Json.createObjectBuilder()
+                .add("objectFooBar", buildJsonObjectFooBar())
+                .add("arrayFooBar", buildJsonArrayFooBar()))
+        .add("arrayOfFooBar", Json.createArrayBuilder()
+            .add(buildJsonObjectFooBar()).add(buildJsonArrayFooBar()))
+        .build();
+    return object;
+  }
+
+  /*********************************************************************************
+   * EXPECTED_SAMPLEJSONOBJECT_TEXT Constant defining expected Json text output
+   * of above sample JsonObject
+   *********************************************************************************/
+  public final static String EXPECTED_SAMPLEJSONOBJECT_TEXT = "{\"firstName\":\"John\",\"lastName\":\"Smith\",\"age\":25,\"elderly\":false,\"patriot\":true,"
+      + "\"address\":{\"streetAddress\":\"21 2nd Street\",\"city\":\"New York\",\"state\":\"NY\","
+      + "\"postalCode\":\"10021\"},\"phoneNumber\":[{\"type\":\"home\",\"number\":\"212 555-1234\"},"
+      + "{\"type\":\"cell\",\"number\":\"646 555-4567\"}],\"objectOfFooBar\":{\"objectFooBar\":"
+      + "{\"foo\":\"bar\"},\"arrayFooBar\":[\"foo\",\"bar\"]},\"arrayOfFooBar\":[{\"foo\":\"bar\"},"
+      + "[\"foo\",\"bar\"]]}";
+
+  /*********************************************************************************
+   * JsonObject createSampleJsonObject2()
+   *********************************************************************************/
+  public static JsonObject createSampleJsonObject2() throws Exception {
+    JsonObject object = Json.createObjectBuilder().add("firstName", "John")
+        .add("lastName", "Smith").add("age", 25).add("elderly", JsonValue.FALSE)
+        .add("patriot", JsonValue.TRUE)
+        .add("address",
+            Json.createObjectBuilder().add("streetAddress", "21 2nd Street")
+                .add("city", "New York").add("state", "NY")
+                .add("postalCode", "10021"))
+        .add("phoneNumber",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("type", "home")
+                    .add("number", "212 555-1234"))
+                .add(Json.createObjectBuilder().add("type", "cell")
+                    .add("number", "535 444-1234")))
+        .build();
+    return object;
+  }
+
+  /*********************************************************************************
+   * JsonArray createSampleJsonArray()
+   *
+   * Assertion ids covered: 400/401/402/403/404/406/409
+   *********************************************************************************/
+  public static JsonArray createSampleJsonArray() throws Exception { // Indices
+    JsonArray array = Json.createArrayBuilder().add(Json.createObjectBuilder() // 0
+        .add("name1", "value1").add("name2", "value2")).add(JsonValue.TRUE)
+        .add(JsonValue.FALSE).add(JsonValue.NULL) // 1,2,3
+        .add(100).add(200L).add("string") // 4,5,6
+        .add(BigDecimal.valueOf(123456789)).add(new BigInteger("123456789"))// 7,8
+        .add(Json.createObjectBuilder() // 9
+            .add("name3", "value3").add("name4", "value4"))
+        .add(true).add(false).addNull() // 10,11,12
+        .add(Json.createArrayBuilder() // 13
+            .add(2).add(4))
+        .add(Json.createObjectBuilder() // 14
+            .add("objectFooBar", buildJsonObjectFooBar())
+            .add("arrayFooBar", buildJsonArrayFooBar()))
+        .add(Json.createArrayBuilder() // 15
+            .add(buildJsonObjectFooBar()).add(buildJsonArrayFooBar()))
+        .build();
+    return array;
+  }
+
+  /*********************************************************************************
+   * EXPECTED_SAMPLEJSONARRAY_TEXT Constant defining expected Json text output
+   * of above sample JsonArray
+   *********************************************************************************/
+  public final static String EXPECTED_SAMPLEJSONARRAY_TEXT = "[{\"name1\":\"value1\",\"name2\":\"value2\"},true,false,null,100,200,\"string\",123456789,123456789,"
+      + "{\"name3\":\"value3\",\"name4\":\"value4\"},true,false,null,[2,4],{\"objectFooBar\":"
+      + "{\"foo\":\"bar\"},\"arrayFooBar\":[\"foo\",\"bar\"]},[{\"foo\":\"bar\"},[\"foo\",\"bar\"]]]";
+
+  /*********************************************************************************
+   * JsonArray createSampleJsonArray2()
+   *********************************************************************************/
+  public static JsonArray createSampleJsonArray2() throws Exception { // Indices
+    JsonArray array = Json.createArrayBuilder().add(Json.createObjectBuilder() // 0
+        .add("name1", "value1").add("name2", "value2")).add(JsonValue.TRUE)
+        .add(JsonValue.FALSE).add(JsonValue.NULL) // 1,2,3
+        .add(Integer.MAX_VALUE).add(Long.MAX_VALUE).add("string") // 4,5,6
+        .add(Json.createObjectBuilder() // 7
+            .add("name3", "value3").add("name4", "value4"))
+        .add(true).add(false).addNull() // 8,9,10
+        .add(Json.createArrayBuilder() // 11
+            .add(1).add(3))
+        .build();
+    return array;
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedInputStream.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedInputStream.java
new file mode 100644
index 0000000..7e59aaf
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedInputStream.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import java.io.*;
+
+// A wrapper class to BufferedInputStream class used to inject IOException errors
+// when the throwIOException instance variable is set. All methods delegate
+// to the parent super class and check whether or not to throw an IOException
+// before delegation.
+
+public class MyBufferedInputStream extends BufferedInputStream {
+
+  private boolean throwIOException = false;
+
+  public MyBufferedInputStream(InputStream in) {
+    super(in);
+  }
+
+  public MyBufferedInputStream(InputStream in, int sz) {
+    super(in, sz);
+  }
+
+  public MyBufferedInputStream(InputStream in, boolean throwIOException) {
+    super(in);
+    this.throwIOException = throwIOException;
+  }
+
+  private void checkToTripIOException() throws IOException {
+    if (throwIOException) {
+      System.out.println(
+          "MyBufferedInputStream->checkToTripIOException: *** tripping an IOException ***");
+      throw new IOException("tripping an IOException");
+    }
+  }
+
+  public void setThrowIOException(boolean throwIOException) {
+    this.throwIOException = throwIOException;
+  }
+
+  public int read() throws IOException {
+    checkToTripIOException();
+    int c = super.read();
+    return c;
+  }
+
+  public int read(byte[] b, int off, int len) throws IOException {
+    checkToTripIOException();
+    int c = super.read(b, off, len);
+    return c;
+  }
+
+  public void close() throws IOException {
+    checkToTripIOException();
+    super.close();
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedReader.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedReader.java
new file mode 100644
index 0000000..00c5145
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedReader.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import java.io.*;
+
+// A wrapper class to BufferedReader class used to inject IOException errors
+// when the throwIOException instance variable is set. All methods delegate
+// to the parent super class and check whether or not to throw an IOException
+// before delegation.
+
+public class MyBufferedReader extends BufferedReader {
+
+  private boolean throwIOException;
+
+  public MyBufferedReader(Reader in) {
+    super(in);
+  }
+
+  public MyBufferedReader(Reader in, int sz) {
+    super(in, sz);
+  }
+
+  public MyBufferedReader(Reader in, int sz, boolean throwIOException) {
+    super(in, sz);
+    this.throwIOException = throwIOException;
+  }
+
+  private void checkToTripIOException() throws IOException {
+    if (throwIOException) {
+      System.out.println("*** tripping an IOException ***");
+      throw new IOException("tripping an IOException");
+    }
+  }
+
+  public void setThrowIOException(boolean throwIOException) {
+    this.throwIOException = throwIOException;
+  }
+
+  public int read() throws IOException {
+    checkToTripIOException();
+    return super.read();
+  }
+
+  public int read(char[] cbuf, int off, int len) throws IOException {
+    checkToTripIOException();
+    return super.read(cbuf, off, len);
+  }
+
+  public String readLine() throws IOException {
+    checkToTripIOException();
+    return super.readLine();
+  }
+
+  public boolean ready() throws IOException {
+    checkToTripIOException();
+    return super.ready();
+  }
+
+  public void reset() throws IOException {
+    checkToTripIOException();
+    super.reset();
+  }
+
+  public void close() throws IOException {
+    checkToTripIOException();
+    super.close();
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedWriter.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedWriter.java
new file mode 100644
index 0000000..a06c99a
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyBufferedWriter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import java.io.*;
+
+// A wrapper class to BufferedWriter class used to inject IOException errors
+// when the throwIOException instance variable is set. All methods delegate
+// to the parent super class and check whether or not to throw an IOException
+// before delegation.
+
+public class MyBufferedWriter extends BufferedWriter {
+
+  private boolean throwIOException;
+
+  public MyBufferedWriter(Writer out) {
+    super(out);
+  }
+
+  public MyBufferedWriter(Writer out, int sz) {
+    super(out, sz);
+  }
+
+  public MyBufferedWriter(Writer out, int sz, boolean throwIOException) {
+    super(out, sz);
+    this.throwIOException = throwIOException;
+  }
+
+  private void checkToTripIOException() throws IOException {
+    if (throwIOException) {
+      System.out.println("*** tripping an IOException ***");
+      throw new IOException("tripping an IOException");
+    }
+  }
+
+  public void setThrowIOException(boolean throwIOException) {
+    this.throwIOException = throwIOException;
+  }
+
+  public void write(int c) throws IOException {
+    checkToTripIOException();
+    super.write(c);
+  }
+
+  public void write(char[] cbuf) throws IOException {
+    checkToTripIOException();
+    super.write(cbuf);
+  }
+
+  public void write(char[] cbuf, int offset, int length) throws IOException {
+    checkToTripIOException();
+    super.write(cbuf, offset, length);
+  }
+
+  public void write(String str) throws IOException {
+    checkToTripIOException();
+    super.write(str);
+  }
+
+  public void write(String str, int offset, int length) throws IOException {
+    checkToTripIOException();
+    super.write(str, offset, length);
+  }
+
+  public void close() throws IOException {
+    checkToTripIOException();
+    super.close();
+  }
+
+  public void flush() throws IOException {
+    checkToTripIOException();
+    super.flush();
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyJsonLocation.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyJsonLocation.java
new file mode 100644
index 0000000..4882ac7
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/common/MyJsonLocation.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.common;
+
+
+import jakarta.json.stream.JsonLocation;
+
+public class MyJsonLocation implements JsonLocation {
+
+  private long lineNumber = -1;
+
+  private long columnNumber = -1;
+
+  private long streamOffset = -1;
+
+  public MyJsonLocation() {
+  }
+
+  public MyJsonLocation(long lineNumber, long columnNumber, long streamOffset) {
+    this.lineNumber = lineNumber;
+    this.columnNumber = columnNumber;
+    this.streamOffset = streamOffset;
+  }
+
+  public void setLineNumber(long lineNumber) {
+    this.lineNumber = lineNumber;
+  }
+
+  public long getLineNumber() {
+    return lineNumber;
+  }
+
+  public void setColumnNumber(long columnNumber) {
+    this.columnNumber = columnNumber;
+  }
+
+  public long getColumnNumber() {
+    return columnNumber;
+  }
+
+  public void setStreamOffset(long streamOffset) {
+    this.streamOffset = streamOffset;
+  }
+
+  public long getStreamOffset() {
+    return streamOffset;
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/lib/harness/Fault.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/lib/harness/Fault.java
new file mode 100644
index 0000000..b7a83e0
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/lib/harness/Fault.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.lib.harness;
+
+import junit.framework.AssertionFailedError;
+
+public class Fault extends AssertionFailedError {
+
+    private static final long serialVersionUID = 1L;
+
+    public Fault() {
+        super();
+    }
+
+    public Fault(String message) {
+        super(message);
+    }
+
+    public Fault(String message, Throwable cause) {
+        super(message + cause);
+    }
+    
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/FileUTFConverter.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/FileUTFConverter.java
new file mode 100644
index 0000000..fe03252
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/FileUTFConverter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+/*
+ * Usage: java FileUTFConverter [-toUTF|-fromUTF] encoding infile outfile
+ *
+ * Utility program for converting a UTF-8 file to various UTF
+ * encoded files and vice-versa.
+ *
+ * Example(s):
+ *
+ * java FileUTFConverter -toUTF UTF-16 jsonObjectUTF8.json jsonObjectUTF16.json
+ *
+ * The above takes a UTF-8 encoded input file (jsonObjectUTF8.json) and converts
+ * it to a UTF-16 encoded output file (jsonObjectUTF16.json).
+ *
+ * java FileUTFConverter -fromUTF UTF-16 jsonObjectUTF16.json jsonObjectUTF8.json
+ *
+ * The above takes a UTF-16 encoded input file (jsonObjectUTF16.json) and
+ * converts it to a UTF-8 encoded output file (jsonObjectUTF8.json).
+ *
+ * All UTF encodings can be used:
+ *
+ * UTF-8
+ * UTF-16
+ * UTF-16BE
+ * UTF-16LE
+ * UTF-32BE
+ * UTF-32LE
+ */
+
+package jakarta.jsonp.tck.util;
+
+import java.io.*;
+
+public class FileUTFConverter {
+
+  private static final String USAGE = "Usage : java FileUTFConverter [-toUTF|-fromUTF] encoding infile outfile";
+
+  public static void main(String args[]) {
+    try {
+      if (args.length != 4) {
+        System.err.println(USAGE);
+        System.exit(1);
+      }
+
+      // Convert UTF-8 input file to specified UTF encoded output file
+      if (args[0].equals("-toUTF")) {
+        System.out
+            .println("FileUTFConverter-> convert UTF-8 encoded input file ("
+                + args[2] + "), to encoding (" + args[1]
+                + ") and write to output file (" + args[3] + ")");
+        FileInputStream fis = new FileInputStream(args[2]);
+        BufferedReader br = new BufferedReader(
+            new InputStreamReader(fis, "UTF-8"));
+        FileOutputStream fos = new FileOutputStream(args[3]);
+        BufferedWriter bw = new BufferedWriter(
+            new OutputStreamWriter(fos, args[1]));
+        for (String s = ""; (s = br.readLine()) != null;) {
+          bw.write(s + System.getProperty("line.separator"));
+          bw.flush();
+        }
+        bw.close();
+        br.close();
+        // Convert specified UTF encoded input file to UTF-8 encoded output file
+      } else if (args[0].equals("-fromUTF")) {
+        System.out.println("FileUTFConverter-> convert UTF encoded input file ("
+            + args[2] + "), from encoding (" + args[1]
+            + ") and write to UTF-8 encoded output file (" + args[3] + ")");
+        FileInputStream fis = new FileInputStream(args[2]);
+        BufferedReader br = new BufferedReader(
+            new InputStreamReader(fis, args[1]));
+        FileOutputStream fos = new FileOutputStream(args[3]);
+        BufferedWriter bw = new BufferedWriter(
+            new OutputStreamWriter(fos, "UTF-8"));
+        for (String s = ""; (s = br.readLine()) != null;) {
+          bw.write(s + System.getProperty("line.separator"));
+          bw.flush();
+        }
+        bw.close();
+        br.close();
+      } else {
+        System.err.println(USAGE);
+        System.exit(1);
+      }
+
+      System.exit(0);
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/MyEncoder.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/MyEncoder.java
new file mode 100644
index 0000000..4ab6483
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/MyEncoder.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.util;
+
+import java.io.*;
+
+/*
+ * Used to generate an encoded file preceded by N null characters.
+ *
+ * This utility takes a valid Json text file as input and generates
+ * an encoded version as output file with N null characters preceded by
+ * each character. For empty files it will just output the N null chars.
+ *
+ */
+public class MyEncoder {
+
+  private static final int NULL = '\0';
+
+  private static final String USAGE = "Usage : java MyEncoder #nulls infile outfile";
+
+  public static void main(String args[]) {
+    if (args.length != 3) {
+      System.err.println(USAGE);
+      System.exit(1);
+    }
+
+    FileReader inputStream = null;
+    FileWriter outputStream = null;
+
+    try {
+      int n = Integer.parseInt(args[0]);
+      inputStream = new FileReader(args[1]);
+      outputStream = new FileWriter(args[2]);
+
+      System.out.println("Null  chars: " + args[0]);
+      System.out.println("Input  file: " + args[1]);
+      System.out.println("Output file: " + args[2]);
+
+      System.out
+          .println("\nCreating an encoded file with each char preceded by " + n
+              + " null chars.\n");
+
+      int c;
+      int nchars = 0;
+      while ((c = inputStream.read()) != -1) {
+        nchars++;
+        for (int i = 0; i < n; i++)
+          outputStream.write(NULL);
+        outputStream.write(c);
+      }
+      if (nchars == 0) {
+        for (int i = 0; i < n; i++)
+          outputStream.write(NULL); // if empty file at least write the nulls
+                                    // out
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    } finally {
+      if (inputStream != null) {
+        try {
+          inputStream.close();
+        } catch (Exception e) {
+        }
+      }
+      if (outputStream != null) {
+        try {
+          outputStream.close();
+        } catch (Exception e) {
+        }
+      }
+    }
+    System.exit(0);
+  }
+}
diff --git a/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/StringUTFConverter.java b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/StringUTFConverter.java
new file mode 100644
index 0000000..cefd268
--- /dev/null
+++ b/tck/tck-common/src/main/java/jakarta/jsonp/tck/util/StringUTFConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+/*
+ * Usage: java StringUTFConverter inputstring encoding outputfile
+ *
+ * Utility program to convert an input string to a UTF encoded output file.
+ *
+ * Example(s):
+ *
+ * java StringUTFConverter "foo" UTF-16LE fooUTF16LE
+ *
+ * The above converts an input string "foo" and outputs to UTF-16LE encoded
+ * output file (fooUTF16LE).
+ */
+
+package jakarta.jsonp.tck.util;
+
+import java.io.*;
+
+public class StringUTFConverter {
+
+  private static final String USAGE = "Usage : java StringUTFConverter inputstring encoding outputfile";
+
+  public static void main(String args[]) {
+    try {
+      if (args.length != 3) {
+        System.err.println(USAGE);
+        System.exit(1);
+      }
+
+      // Convert string to specified UTF encoded output file
+      System.out.println(
+          "StringtoUTF-> convert string (" + args[0] + "), to encoding ("
+              + args[1] + ") and write to output file (" + args[2] + ")");
+      FileOutputStream fos = new FileOutputStream(args[2]);
+      BufferedWriter bw = new BufferedWriter(
+          new OutputStreamWriter(fos, args[1]));
+      bw.write(args[0]);
+      bw.flush();
+      bw.close();
+
+      System.exit(0);
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+  }
+}
diff --git a/tck/tck-tests-plugability/pom.xml b/tck/tck-tests-plugability/pom.xml
new file mode 100644
index 0000000..9ffbf88
--- /dev/null
+++ b/tck/tck-tests-plugability/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>jakarta.json</groupId>
+        <artifactId>jakarta.json-tck</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jakarta.json-tck-tests-plugability</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-common</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/pluggability/jsonprovidertests/ClientTests.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/pluggability/jsonprovidertests/ClientTests.java
new file mode 100644
index 0000000..58310ed
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/pluggability/jsonprovidertests/ClientTests.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.pluggability.jsonprovidertests;
+
+import jakarta.json.*;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+import jakarta.jsonp.tck.provider.MyJsonProvider;
+import jakarta.jsonp.tck.provider.MyJsonGenerator;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+
+  private static final String MY_JSONPROVIDER_CLASS = "jakarta.jsonp.tck.provider.MyJsonProvider";
+
+  private String providerPath = null;
+  
+  @After
+  public void after() {
+      MyJsonProvider.clearCalls();
+      MyJsonGenerator.clearCalls();
+  }
+
+  /* Tests */
+
+  /*
+   * @testName: jsonProviderTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:152;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * static JsonProvider provider()
+   */
+  @Test
+  public void jsonProviderTest1() throws Fault {
+    boolean pass = true;
+    try {
+      // Load my provider
+      JsonProvider provider = JsonProvider.provider();
+      String providerClass = provider.getClass().getName();
+      System.out.println("provider class=" + providerClass);
+      if (providerClass.equals(MY_JSONPROVIDER_CLASS))
+        System.out.println("Current provider is my provider - expected.");
+      else {
+        System.err.println("Current provider is not my provider - unexpected.");
+        pass = false;
+        ServiceLoader<JsonProvider> loader = ServiceLoader.load(JsonProvider.class);
+        Iterator<JsonProvider> it = loader.iterator();
+        List<JsonProvider> providers = new ArrayList<>();
+        while(it.hasNext()) {
+            providers.add(it.next());
+        }
+        System.out.println("Providers: "+providers);
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:144;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonGenerator createGenerator(Writer)
+   */
+  @Test
+  public void jsonProviderTest2() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonGenerator createGenerator(Writer)";
+    String expString2 = "public JsonGenerator writeStartArray()";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonGenerator generator = Json.createGenerator(new StringWriter());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+      generator.writeStartArray();
+      String actString2 = MyJsonGenerator.getCalls();
+      System.out.println("Verify SPI generator method was called: " + expString2);
+      pass = JSONP_Util.assertEquals(expString2, actString2);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:192;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonGenerator createGenerator(OutputStream)
+   */
+  @Test
+  public void jsonProviderTest3() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonGenerator createGenerator(OutputStream)";
+    String expString2 = "public JsonGenerator writeStartObject()";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonGenerator generator = Json
+          .createGenerator(new ByteArrayOutputStream());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+      generator.writeStartObject();
+      String actString2 = MyJsonGenerator.getCalls();
+      System.out.println("Verify SPI generator method was called: " + expString2);
+      pass = JSONP_Util.assertEquals(expString2, actString2);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:146;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonParser createParser(Reader)
+   */
+  @Test
+  public void jsonProviderTest4() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonParser createParser(Reader)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonParser parser = Json.createParser(new StringReader("{}"));
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:196;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonParser createParser(InputStream)
+   */
+  @Test
+  public void jsonProviderTest5() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonParser createParser(InputStream)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonParser parser = Json
+          .createParser(JSONP_Util.getInputStreamFromString("{}"));
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest5 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest6
+   * 
+   * @assertion_ids: JSONP:JAVADOC:465;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonParserFactory createParserFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest6() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonParserFactory createParserFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest6 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest6 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest7
+   * 
+   * @assertion_ids: JSONP:JAVADOC:426;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonParserFactory createParserFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest7() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonParserFactory createParserFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(new HashMap<String, Object>());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest7 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest7 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest8
+   * 
+   * @assertion_ids: JSONP:JAVADOC:425;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonGeneratorFactory createGeneratorFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest8() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonGeneratorFactory createGeneratorFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonGeneratorFactory generatorFactory = Json
+          .createGeneratorFactory(new HashMap<String, Object>());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest8 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest8 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest9
+   * 
+   * @assertion_ids: JSONP:JAVADOC:472;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonWriterFactory createWriterFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest9() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonWriterFactory createWriterFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonWriterFactory factory = Json
+          .createWriterFactory(JSONP_Util.getEmptyConfig());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest9 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest9 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest10
+   * 
+   * @assertion_ids: JSONP:JAVADOC:223;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonParser createParser(InputStream) Tests the case where a JsonException
+   * can be thrown. An InputStream of null will cause MyJsonProvider to throw
+   * JsonException.
+   */
+  @Test
+  public void jsonProviderTest10() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonParser createParser(InputStream)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      InputStream in = null;
+      JsonParser parser = Json.createParser(in);
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException: " + e);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest10 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest10 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest11
+   * 
+   * @assertion_ids: JSONP:JAVADOC:464;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonArrayBuilder createArrayBuilder()
+   */
+  @Test
+  public void jsonProviderTest11() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonArrayBuilder createArrayBuilder()";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest11 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest11 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest12
+   * 
+   * @assertion_ids: JSONP:JAVADOC:466;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonObjectBuilder createObjectBuilder()
+   */
+  @Test
+  public void jsonProviderTest12() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonObjectBuilder createObjectBuilder()";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest12 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest12 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest13
+   * 
+   * @assertion_ids: JSONP:JAVADOC:465;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonBuilderFactory createBuilderFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest13() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonBuilderFactory createBuilderFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonBuilderFactory objectBuilder = Json
+          .createBuilderFactory(JSONP_Util.getEmptyConfig());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest13 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest13 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest14
+   * 
+   * @assertion_ids: JSONP:JAVADOC:467;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonReader createReader(Reader)
+   */
+  @Test
+  public void jsonProviderTest14() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonReader createReader(Reader)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonReader reader = Json.createReader(new StringReader("{}"));
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest14 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest14 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest15
+   * 
+   * @assertion_ids: JSONP:JAVADOC:468;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonReader createReader(InputStream)
+   */
+  @Test
+  public void jsonProviderTest15() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonReader createReader(InputStream)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonReader reader = Json
+          .createReader(JSONP_Util.getInputStreamFromString("{}"));
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest15 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest15 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest16
+   * 
+   * @assertion_ids: JSONP:JAVADOC:470;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonWriter createWriter(Writer)
+   */
+  @Test
+  public void jsonProviderTest16() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonWriter createWriter(Writer)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonWriter writer = Json.createWriter(new StringWriter());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest16 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest16 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest17
+   * 
+   * @assertion_ids: JSONP:JAVADOC:471;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonWriter createWriter(OutputStream)
+   */
+  @Test
+  public void jsonProviderTest17() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonWriter createWriter(OutputStream)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonWriter writer = Json.createWriter(new ByteArrayOutputStream());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest17 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest17 Failed");
+  }
+
+  /*
+   * @testName: jsonProviderTest18
+   * 
+   * @assertion_ids: JSONP:JAVADOC:469;
+   * 
+   * @test_Strategy: Test call of SPI provider method with signature: o public
+   * JsonReaderFactory createReaderFactory(Map<String, ?>)
+   */
+  @Test
+  public void jsonProviderTest18() throws Fault {
+    boolean pass = true;
+    String expString = "public JsonReaderFactory createReaderFactory(Map<String, ?>)";
+    try {
+      System.out.println("Calling SPI provider method: " + expString);
+      JsonReaderFactory factory = Json
+          .createReaderFactory(JSONP_Util.getEmptyConfig());
+      String actString = MyJsonProvider.getCalls();
+      System.out.println("Verify SPI provider method was called: " + expString);
+      pass = JSONP_Util.assertEquals(expString, actString);
+    } catch (Exception e) {
+      throw new Fault("jsonProviderTest18 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonProviderTest18 Failed");
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGenerator.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGenerator.java
new file mode 100644
index 0000000..9340070
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGenerator.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+/*
+ * MyJsonGenerator is a Json Test Generator used by the pluggability tests
+ * to test the Json SPI layer. This generator tracks that the proper callback
+ * methods are invoked within the generator when Json API methods are called.
+ */
+
+public class MyJsonGenerator implements JsonGenerator {
+  private Writer writer = null;
+
+  private OutputStream out = null;
+
+  private final Charset charset = Charset.forName("UTF-8");
+
+  private void dumpInstanceVars() {
+    System.out.println("writer=" + writer);
+    System.out.println("out=" + out);
+    System.out.println("charset=" + charset);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonGenerator(Writer writer) {
+    this.writer = writer;
+  }
+
+  public MyJsonGenerator(OutputStream out) {
+    this.out = out;
+  }
+
+  @Override
+  public void flush() {
+    System.out.println("public void flush()");
+    addCalls("public void flush()");
+  }
+
+  @Override
+  public JsonGenerator writeStartObject() {
+    System.out.println("public JsonGenerator writeStartObject()");
+    addCalls("public JsonGenerator writeStartObject()");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeStartObject(String name) {
+    System.out.println("public JsonGenerator writeStartObject(String)");
+    addCalls("public JsonGenerator writeStartObject(String)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, String value) {
+    System.out.println("public JsonGenerator write(String,String)");
+    addCalls("public JsonGenerator write(String,String)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, int value) {
+    System.out.println("public JsonGenerator write(String,int)");
+    addCalls("public JsonGenerator write(String,int)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, long value) {
+    System.out.println("public JsonGenerator write(String,long)");
+    addCalls("public JsonGenerator write(String,long)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, double value) {
+    System.out.println("public JsonGenerator write(String,double)");
+    addCalls("public JsonGenerator write(String,double)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, BigInteger value) {
+    System.out.println("public JsonGenerator write(String,BigInteger)");
+    addCalls("public JsonGenerator write(String,BigInteger)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, BigDecimal value) {
+    System.out.println("public JsonGenerator write(String,BigDecimal)");
+    addCalls("public JsonGenerator write(String,BigDecimal)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, boolean value) {
+    System.out.println("public JsonGenerator write(String,boolean)");
+    addCalls("public JsonGenerator write(String,boolean)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String name, JsonValue value) {
+    System.out.println("public JsonGenerator write(String,JsonValue)");
+    addCalls("public JsonGenerator write(String,JsonValue)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeNull(String name) {
+    System.out.println("public JsonGenerator writeNull(String)");
+    addCalls("public JsonGenerator writeNull(String)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeStartArray() {
+    System.out.println("public JsonGenerator writeStartArray()");
+    addCalls("public JsonGenerator writeStartArray()");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeStartArray(String name) {
+    System.out.println("public JsonGenerator writeStartArray(String)");
+    addCalls("public JsonGenerator writeStartArray(String)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(String value) {
+    System.out.println("public JsonGenerator write(String)");
+    addCalls("public JsonGenerator write(String)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(int value) {
+    System.out.println("public JsonGenerator write(int)");
+    addCalls("public JsonGenerator write(int)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(long value) {
+    System.out.println("public JsonGenerator write(long)");
+    addCalls("public JsonGenerator write(long)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(double value) {
+    System.out.println("public JsonGenerator write(double)");
+    addCalls("public JsonGenerator write(double)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(BigInteger value) {
+    System.out.println("public JsonGenerator write(BigInteger)");
+    addCalls("public JsonGenerator write(BigInteger)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(BigDecimal value) {
+    System.out.println("public JsonGenerator write(BigDecimal)");
+    addCalls("public JsonGenerator write(BigDecimal)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(boolean value) {
+    System.out.println("public JsonGenerator write(boolean)");
+    addCalls("public JsonGenerator write(boolean)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator write(JsonValue value) {
+    System.out.println("public JsonGenerator write(JsonValue)");
+    addCalls("public JsonGenerator write(JsonValue)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeNull() {
+    System.out.println("public JsonGenerator writeNull()");
+    addCalls("public JsonGenerator writeNull()");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeEnd() {
+    System.out.println("public JsonGenerator writeEnd()");
+    addCalls("public JsonGenerator writeEnd()");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator writeKey(String name) {
+    System.out.println("public JsonGenerator writeKey()");
+    addCalls("public JsonGenerator writeKey()");
+    return null;
+  }
+
+  @Override
+  public void close() {
+    System.out.println("public void close()");
+    addCalls("public void close()");
+  }
+
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGeneratorFactory.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGeneratorFactory.java
new file mode 100644
index 0000000..d5ae3dd
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonGeneratorFactory.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonGeneratorFactory is a Json Test GeneratorFactory used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+
+public class MyJsonGeneratorFactory implements JsonGeneratorFactory {
+  private OutputStream out = null;
+
+  private Writer writer = null;
+
+  private Charset charset = Charset.forName("UTF-8");
+
+  private Map<String, ?> config = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("writer=" + writer);
+    System.out.println("out=" + out);
+    System.out.println("charset=" + charset);
+    System.out.println("config=" + config);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonGeneratorFactory(Map<String, ?> config) {
+    this.config = config;
+  }
+
+  public Map<String, ?> getConfigInUse() {
+    System.out.println("public Map<String, ?> getConfigInUse()");
+    addCalls("public Map<String, ?> getConfigInUse()");
+    return config;
+  }
+
+  public JsonGenerator createGenerator(OutputStream out) {
+    System.out.println("public JsonGenerator createGenerator(OutputStream)");
+    addCalls("public JsonGenerator createGenerator(OutputStream)");
+    this.out = out;
+    return null;
+  }
+
+  public JsonGenerator createGenerator(OutputStream out, Charset charset) {
+    System.out.println(
+        "public JsonGenerator createGenerator(OutputStream, Charset)");
+    addCalls("public JsonGenerator createGenerator(OutputStream, Charset)");
+    this.out = out;
+    this.charset = charset;
+    return null;
+  }
+
+  public JsonGenerator createGenerator(Writer writer) {
+    System.out.println("public JsonGenerator createGenerator(Writer)");
+    addCalls("public JsonGenerator createGenerator(Writer)");
+    this.writer = writer;
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParser.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParser.java
new file mode 100644
index 0000000..5df3fc5
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParser.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import jakarta.json.spi.JsonProvider;
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonParser is a Json Test Parser used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+
+public class MyJsonParser implements JsonParser {
+  private InputStream in = null;
+
+  private Reader reader = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("reader=" + reader);
+    System.out.println("in=" + in);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonParser(InputStream in) {
+    this.in = in;
+  }
+
+  public MyJsonParser(Reader reader) {
+    this.reader = reader;
+  }
+
+  public void close() {
+    System.out.println("public void close()");
+    addCalls("public void close()");
+  }
+
+  public BigDecimal getBigDecimal() {
+    System.out.println("public BigDecimal getBigDecimal()");
+    addCalls("public BigDecimal getBigDecimal()");
+    return null;
+  }
+
+  public int getInt() {
+    System.out.println("public int getInt()");
+    addCalls("public int getInt()");
+    return -1;
+  }
+
+  public JsonLocation getLocation() {
+    System.out.println("public JsonLocation getLocation()");
+    addCalls("public JsonLocation getLocation()");
+    return null;
+  }
+
+  public long getLong() {
+    System.out.println("public long getLong()");
+    addCalls("public long getLong()");
+    return -1;
+  }
+
+  public boolean isIntegralNumber() {
+    System.out.println("public boolean isIntegralNumber()");
+    addCalls("public boolean isIntegralNumber()");
+    return false;
+  }
+
+  public String getString() {
+    System.out.println("public String getString()");
+    addCalls("public String getString()");
+    return null;
+  }
+
+  public boolean hasNext() {
+    System.out.println("public boolean hasNext()");
+    addCalls("public boolean hasNext()");
+    return false;
+  }
+
+  public JsonParser.Event next() {
+    System.out.println("public JsonParser.Event next()");
+    addCalls("public JsonParser.Event next()");
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParserFactory.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParserFactory.java
new file mode 100644
index 0000000..0d06523
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonParserFactory.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonParserFactory is a Json Test ParserFactory used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+
+public class MyJsonParserFactory implements JsonParserFactory {
+  private InputStream in = null;
+
+  private Charset charset = null;
+
+  private Reader reader = null;
+
+  private Map<String, ?> config = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("reader=" + reader);
+    System.out.println("in=" + in);
+    System.out.println("charset=" + charset);
+    System.out.println("config=" + config);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonParserFactory(Map<String, ?> config) {
+    this.config = config;
+  }
+
+  public Map<String, ?> getConfigInUse() {
+    System.out.println("public Map<String, ?> getConfigInUse()");
+    addCalls("public Map<String, ?> getConfigInUse()");
+    return config;
+  }
+
+  public JsonParser createParser(InputStream in) {
+    System.out.println("public JsonParser createParser(InputStream)");
+    addCalls("public JsonParser createParser(InputStream)");
+    this.in = in;
+    return null;
+  }
+
+  public JsonParser createParser(InputStream in, Charset charset) {
+    System.out.println("public JsonParser createParser(InputStream, Charset)");
+    addCalls("public JsonParser createParser(InputStream, Charset)");
+    this.in = in;
+    this.charset = charset;
+    return null;
+  }
+
+  public JsonParser createParser(Reader reader) {
+    System.out.println("public JsonParser createParser(Reader)");
+    addCalls("public JsonParser createParser(Reader)");
+    this.reader = reader;
+    return null;
+  }
+
+  public JsonParser createParser(JsonArray jsonArray) {
+    System.out.println("public JsonParser createParser(JsonArray)");
+    addCalls("public JsonParser createParser(JsonArray)");
+    return null;
+  }
+
+  public JsonParser createParser(JsonObject jsonObject) {
+    System.out.println("public JsonParser createParser(JsonObject)");
+    addCalls("public JsonParser createParser(JsonObject)");
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonProvider.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonProvider.java
new file mode 100644
index 0000000..c7d1680
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonProvider.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import java.io.*;
+import java.util.*;
+import jakarta.json.*;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.*;
+
+// $Id$
+/*
+ * MyJsonProvider is a Json Test Provider used by the pluggability tests
+ * to test the Json SPI layer. This provider tracks that the proper callback
+ * methods are invoked within the provider when Json API methods are called.
+ */
+public class MyJsonProvider extends JsonProvider {
+
+  // Exception thrown when encoding or i/o error
+  private final JsonException exception = new JsonException(
+      "encoding or i/o error");
+
+  // call methods
+  private static final StringBuilder CALLS = new StringBuilder();
+
+  public static String getCalls() {
+    return CALLS.toString();
+  }
+
+  public static void clearCalls() {
+    CALLS.delete(0, CALLS.length());
+  }
+
+  private static void addCalls(String s) {
+    CALLS.append(s);
+  }
+
+  @Override
+  public JsonArrayBuilder createArrayBuilder() {
+    System.out.println("public JsonArrayBuilder createArrayBuilder()");
+    addCalls("public JsonArrayBuilder createArrayBuilder()");
+    return null;
+  }
+
+  @Override
+  public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
+    System.out.println(
+        "public JsonBuilderFactory createBuilderFactory(Map<String, ?>)");
+    addCalls("public JsonBuilderFactory createBuilderFactory(Map<String, ?>)");
+    return null;
+  }
+
+  @Override
+  public JsonObjectBuilder createObjectBuilder() {
+    System.out.println("public JsonObjectBuilder createObjectBuilder()");
+    addCalls("public JsonObjectBuilder createObjectBuilder()");
+    return null;
+  }
+
+  @Override
+  public JsonReader createReader(Reader reader) {
+    System.out.println("public JsonReader createReader(Reader)");
+    addCalls("public JsonReader createReader(Reader)");
+    return new MyJsonReader(reader);
+  }
+
+  @Override
+  public JsonReader createReader(InputStream in) {
+    System.out.println("public JsonReader createReader(InputStream)");
+    addCalls("public JsonReader createReader(InputStream)");
+    return new MyJsonReader(in);
+  }
+
+  @Override
+  public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
+    System.out.println(
+        "public JsonReaderFactory createReaderFactory(Map<String, ?>)");
+    addCalls("public JsonReaderFactory createReaderFactory(Map<String, ?>)");
+    return null;
+  }
+
+  @Override
+  public JsonWriter createWriter(Writer writer) {
+    System.out.println("public JsonWriter createWriter(Writer)");
+    addCalls("public JsonWriter createWriter(Writer)");
+    return new MyJsonWriter(writer);
+  }
+
+  @Override
+  public JsonWriter createWriter(OutputStream out) {
+    System.out.println("public JsonWriter createWriter(OutputStream)");
+    addCalls("public JsonWriter createWriter(OutputStream)");
+    return new MyJsonWriter(out);
+  }
+
+  @Override
+  public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
+    System.out.println(
+        "public JsonWriterFactory createWriterFactory(Map<String, ?>)");
+    addCalls("public JsonWriterFactory createWriterFactory(Map<String, ?>)");
+    return null;
+  }
+
+  @Override
+  public JsonGenerator createGenerator(Writer writer) {
+    System.out.println("public JsonGenerator createGenerator(Writer)");
+    addCalls("public JsonGenerator createGenerator(Writer)");
+    return new MyJsonGenerator(writer);
+  }
+
+  @Override
+  public JsonGenerator createGenerator(OutputStream out) {
+    System.out.println("public JsonGenerator createGenerator(OutputStream)");
+    addCalls("public JsonGenerator createGenerator(OutputStream)");
+    return new MyJsonGenerator(out);
+  }
+
+  @Override
+  public JsonParser createParser(Reader reader) {
+    System.out.println("public JsonParser createParser(Reader)");
+    addCalls("public JsonParser createParser(Reader)");
+    return new MyJsonParser(reader);
+  }
+
+  @Override
+  public JsonParser createParser(InputStream in) {
+    System.out.println("public JsonParser createParser(InputStream)");
+    addCalls("public JsonParser createParser(InputStream)");
+    if (in == null)
+      throw exception;
+    else
+      return new MyJsonParser(in);
+  }
+
+  @Override
+  public JsonParserFactory createParserFactory(Map<String, ?> config) {
+    System.out.println(
+        "public JsonParserFactory createParserFactory(Map<String, ?>)");
+    addCalls("public JsonParserFactory createParserFactory(Map<String, ?>)");
+    return null;
+  }
+
+  @Override
+  public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
+    System.out.println(
+        "public JsonGeneratorFactory createGeneratorFactory(Map<String, ?>)");
+    addCalls(
+        "public JsonGeneratorFactory createGeneratorFactory(Map<String, ?>)");
+    return null;
+  }
+
+  @Override
+  public JsonPatchBuilder createPatchBuilder() {
+    throw new UnsupportedOperationException("Not supported yet."); // To change
+                                                                   // body of
+                                                                   // generated
+                                                                   // methods,
+                                                                   // choose
+                                                                   // Tools |
+                                                                   // Templates.
+  }
+
+  @Override
+  public JsonPatchBuilder createPatchBuilder(JsonArray ja) {
+    throw new UnsupportedOperationException("Not supported yet."); // To change
+                                                                   // body of
+                                                                   // generated
+                                                                   // methods,
+                                                                   // choose
+                                                                   // Tools |
+                                                                   // Templates.
+  }
+
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReader.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReader.java
new file mode 100644
index 0000000..a74c203
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReader.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import jakarta.json.spi.JsonProvider;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonReader is a Json Test Reader used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+
+public class MyJsonReader implements JsonReader {
+  private InputStream in = null;
+
+  private Reader reader = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("reader=" + reader);
+    System.out.println("in=" + in);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonReader() {
+  }
+
+  public MyJsonReader(InputStream in) {
+    this.in = in;
+  }
+
+  public MyJsonReader(Reader reader) {
+    this.reader = reader;
+  }
+
+  public void close() {
+    System.out.println("public void close()");
+    addCalls("public void close()");
+  }
+
+  public JsonStructure read() {
+    System.out.println("public void read()");
+    addCalls("public void read()");
+    return null;
+  }
+
+  public JsonArray readArray() {
+    System.out.println("public void readArray()");
+    addCalls("public void readArray()");
+    return null;
+  }
+
+  public JsonObject readObject() {
+    System.out.println("public void readObject()");
+    addCalls("public void readObject()");
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReaderFactory.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReaderFactory.java
new file mode 100644
index 0000000..d58b9a2
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonReaderFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import jakarta.json.*;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonReaderFactory is a Json Test ReaderFactory used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+public class MyJsonReaderFactory implements JsonReaderFactory {
+  private InputStream in = null;
+
+  private Charset charset = null;
+
+  private Reader reader = null;
+
+  private Map<String, ?> config = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("reader=" + reader);
+    System.out.println("in=" + in);
+    System.out.println("charset=" + charset);
+    System.out.println("config=" + config);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonReaderFactory(Map<String, ?> config) {
+    this.config = config;
+  }
+
+  public Map<String, ?> getConfigInUse() {
+    System.out.println("public Map<String, ?> getConfigInUse()");
+    addCalls("public Map<String, ?> getConfigInUse()");
+    return config;
+  }
+
+  public JsonReader createReader(InputStream in) {
+    System.out.println("public JsonReader createReader(InputStream)");
+    addCalls("public JsonReader createReader(InputStream)");
+    this.in = in;
+    return null;
+  }
+
+  public JsonReader createReader(InputStream in, Charset charset) {
+    System.out.println("public JsonReader createReader(InputStream, Charset)");
+    addCalls("public JsonReader createReader(InputStream, Charset)");
+    this.in = in;
+    this.charset = charset;
+    return null;
+  }
+
+  public JsonReader createReader(Reader reader) {
+    System.out.println("public JsonReader createReader(Reader)");
+    addCalls("public JsonReader createReader(Reader)");
+    this.reader = reader;
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriter.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriter.java
new file mode 100644
index 0000000..a3321f0
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import jakarta.json.spi.JsonProvider;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonWriter is a Json Test Writer used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+public class MyJsonWriter implements JsonWriter {
+  private OutputStream out = null;
+
+  private Writer writer = null;
+
+  private Charset charset = Charset.forName("UTF-8");
+
+  private void dumpInstanceVars() {
+    System.out.println("writer=" + writer);
+    System.out.println("out=" + out);
+    System.out.println("charset=" + charset);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonWriter() {
+  }
+
+  public MyJsonWriter(OutputStream out) {
+    this.out = out;
+  }
+
+  public MyJsonWriter(Writer writer) {
+    this.writer = writer;
+  }
+
+  public void close() {
+    System.out.println("public void close()");
+    addCalls("public void close()");
+  }
+
+  public void write(JsonStructure value) {
+    System.out.println("public void write(JsonStructure)");
+    addCalls("public void write(JsonStructure)");
+  }
+
+  public void writeArray(JsonArray array) {
+    System.out.println("public void writeArray(JsonArray)");
+    addCalls("public void writeArray(JsonArray)");
+  }
+
+  public void writeObject(JsonObject object) {
+    System.out.println("public void writeObject(JsonObject)");
+    addCalls("public void writeObject(JsonObject)");
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriterFactory.java b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriterFactory.java
new file mode 100644
index 0000000..f15cfbd
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/java/jakarta/jsonp/tck/provider/MyJsonWriterFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+
+package jakarta.jsonp.tck.provider;
+
+
+import jakarta.json.*;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+
+/*
+ * MyJsonWriterFactory is a Json Test WriterFactory used by the pluggability tests
+ * to test the Json SPI layer. This parser tracks that the proper callback
+ * methods are invoked within the parser when Json API methods are called.
+ */
+public class MyJsonWriterFactory implements JsonWriterFactory {
+  private OutputStream out = null;
+
+  private Writer writer = null;
+
+  private Charset charset = null;
+
+  private Map<String, ?> config = null;
+
+  private void dumpInstanceVars() {
+    System.out.println("writer=" + writer);
+    System.out.println("out=" + out);
+    System.out.println("charset=" + charset);
+    System.out.println("config=" + config);
+  }
+
+  // call methods
+  private static StringBuilder calls = new StringBuilder();
+
+  public static String getCalls() {
+    return calls.toString();
+  }
+
+  public static void clearCalls() {
+    calls.delete(0, calls.length());
+  }
+
+  private static void addCalls(String s) {
+    calls.append(s);
+  }
+
+  public MyJsonWriterFactory(Map<String, ?> config) {
+    this.config = config;
+  }
+
+  public Map<String, ?> getConfigInUse() {
+    System.out.println("public Map<String, ?> getConfigInUse()");
+    addCalls("public Map<String, ?> getConfigInUse()");
+    return config;
+  }
+
+  public JsonWriter createWriter(OutputStream out) {
+    System.out.println("public JsonWriter createWriter(OutputStream)");
+    addCalls("public JsonWriter createWriter(OutputStream)");
+    this.out = out;
+    return null;
+  }
+
+  public JsonWriter createWriter(OutputStream out, Charset charset) {
+    System.out.println("public JsonWriter createWriter(OutputStream, Charset)");
+    addCalls("public JsonWriter createWriter(OutputStream, Charset)");
+    this.out = out;
+    this.charset = charset;
+    return null;
+  }
+
+  public JsonWriter createWriter(Writer writer) {
+    System.out.println("public JsonWriter createWriter(Writer)");
+    addCalls("public JsonWriter createWriter(Writer)");
+    this.writer = writer;
+    return null;
+  }
+}
diff --git a/tck/tck-tests-plugability/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider b/tck/tck-tests-plugability/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
new file mode 100644
index 0000000..997ae9a
--- /dev/null
+++ b/tck/tck-tests-plugability/src/main/resources/META-INF/services/jakarta.json.spi.JsonProvider
@@ -0,0 +1 @@
+jakarta.jsonp.tck.provider.MyJsonProvider
diff --git a/tck/tck-tests/pom.xml b/tck/tck-tests/pom.xml
new file mode 100644
index 0000000..af1c6d0
--- /dev/null
+++ b/tck/tck-tests/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>jakarta.json</groupId>
+        <artifactId>jakarta.json-tck</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jakarta.json-tck-tests</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-tck-common</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/CollectorTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/CollectorTests.java
new file mode 100644
index 0000000..a15bc5e
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/CollectorTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.collectortests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) Pointer compatibility tests.<br>
+ * Test {@link jakarta.json.stream.JsonCollectors} class implementation.
+ */
+@RunWith(Arquillian.class)
+public class CollectorTests {
+    
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, CollectorTests.class.getPackage().getName());
+    }
+
+  /**
+   * Test JSON-P {@link jakarta.json.stream.JsonCollectors} class implementation.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonCollectorTest
+   * @assertion_ids: JSONP:JAVADOC:668; JSONP:JAVADOC:669; JSONP:JAVADOC:670;
+   *                 JSONP:JAVADOC:671;
+   * @test_Strategy: Test all collectors returned by API.
+   */
+  @Test
+  public void jsonCollectorTest() throws Fault {
+    Collectors collectorTest = new Collectors();
+    final TestResult result = collectorTest.test();
+    result.eval();
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/Collectors.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/Collectors.java
new file mode 100644
index 0000000..bffc445
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/collectortests/Collectors.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.collectortests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonCollectors;
+import jakarta.json.stream.JsonParser;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) Pointer compatibility tests.<br>
+ * Test {@link jakarta.json.stream.JsonCollectors} class implementation. This
+ * class was added to resolve
+ * {@see <a href="https://java.net/jira/browse/JSON_PROCESSING_SPEC-68">RFE:
+ * Support JSON queries using JDK's stream operations</a>}.
+ */
+public class Collectors {
+
+  /** Tests input data with JsonArray instances. */
+  private static final JsonArray[] ARRAY_VALUES = new JsonArray[] {
+      createSimpleStringArray5(), // JsonArray with String
+      createSimpleIntArray5(), // JsonArray with int
+      createSimpleBoolArray5(), // JsonArray with boolean
+      createSimpleObjectArray5() // JsonArray with JsonObject
+  };
+
+  /** Tests input data with JsonArray instances. */
+  private static final JsonObject[] OBJ_VALUES = new JsonObject[] {
+      createSimpleObjectWithStr(), // JsonObject with String
+      createSimpleObjectWithInt(), // JsonObject with int
+      createSimpleObjectWithBool(), // JsonObject with boolean
+      createCompoundObject() // JsonObject with JsonObject
+  };
+
+  /** Test input data for {@code groupingBy} methods. */
+  private static final JsonArray OBJ_ARRAY_GROUP = Json.createArrayBuilder()
+      .add(Json.createObjectBuilder().add("name", "Peter").add("office",
+          "Green"))
+      .add(Json.createObjectBuilder().add("name", "John").add("office", "Red"))
+      .add(Json.createObjectBuilder().add("name", "Bob").add("office", "Blue"))
+      .add(Json.createObjectBuilder().add("name", "Sarah").add("office", "Red"))
+      .add(Json.createObjectBuilder().add("name", "Tom").add("office", "Blue"))
+      .add(Json.createObjectBuilder().add("name", "Jane").add("office", "Blue"))
+      .add(Json.createObjectBuilder().add("name", "Peggy").add("office",
+          "Green"))
+      .add(Json.createObjectBuilder().add("name", "Rick").add("office", "Red"))
+      .build();
+
+  /**
+   * Creates an instance of {@link jakarta.json.stream.JsonCollectors} class
+   * implementation tests.
+   */
+  Collectors() {
+    super();
+  }
+
+  /**
+   * Test {@link jakarta.json.stream.JsonCollectors} class implementation. Suite
+   * entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonCollectors class implementation");
+    System.out.println("JsonCollectors class implementation");
+    testToJsonArrayCollector(result);
+    // testSimpleToJsonObjectCollector(result);
+    testToJsonObjectCollector(result);
+    testSimpleGroupingByCollector(result);
+    testSortingGroupingByCollector(result);
+    return result;
+  }
+
+  /**
+   * Test collector returned by {@code toJsonArray()} method. This collector
+   * packs {@code Stream<JsonValue>} contend into a single JsonArray instance
+   * which contains stream values in the same order at they were read. It can be
+   * considered as {@link JsonParser#getArrayStream()} counterpart.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testToJsonArrayCollector(final TestResult result) {
+    System.out.println(" - Collector returned by toJsonArray()");
+    for (final JsonArray in : ARRAY_VALUES) {
+      System.out.println("   - Input: " + valueToString(in));
+      final Collector<JsonValue, JsonArrayBuilder, JsonArray> col = JsonCollectors
+          .toJsonArray();
+      final JsonArray out = in.getValuesAs(JsonObject.class).stream()
+          .collect(col);
+      if (operationFailed(in, out)) {
+        result.fail("toJsonArray()", "Output Stream value " + valueToString(out)
+            + " shall be " + valueToString(in));
+      }
+    }
+  }
+
+  // TCK test for https://java.net/jira/browse/JSON_PROCESSING_SPEC-82 in case
+  // it will be checked in.
+  // /**
+  // * Test collector returned by {@code toJsonObject()} method.
+  // * This collector packs {@code Stream<JsonValue>} contend into a single
+  // JsonArray instance which contains
+  // * stream values in the same order at they were read. It can be considered
+  // as {@link JsonParser#getArrayStream()}
+  // * counterpart.
+  // * @param result Tests result record.
+  // */
+  // private void testSimpleToJsonObjectCollector(final TestResult result) {
+  // System.out.println(" - Collector returned by toJsonObject()");
+  // for (final JsonObject in : OBJ_VALUES) {
+  // System.out.println(" - Input: " + valueToString(in));
+  // final Collector<Map.Entry<String,JsonValue>, JsonObjectBuilder, JsonObject>
+  // col = JsonCollectors.toJsonObject();
+  // final JsonObject out = (in.entrySet()).stream().collect(col);
+  // if (operationFailed(in, out)) {
+  // result.fail("toJsonObject()",
+  // "Output Stream value " + valueToString(out) + " shall be " +
+  // valueToString(in));
+  // }
+  // }
+  // }
+
+  /**
+   * Test collector returned by
+   * {@code toJsonObject(Function<JsonValue,String>, Function<JsonValue,JsonValue>)}
+   * method. This collector does not pack content of
+   * {@code Stream<Map.Entry<String,JsonValue>>} stream of
+   * {@link JsonParser#getObjectStream()} output. So it's not counterpart of
+   * this method. It works with {@code Stream<JsonValue>}, which is
+   * {@link JsonParser#getArrayStream()} output and uses two {@link Function}
+   * implementations to build target object keys and values from
+   * {@code JsonValue} in the {@code Stream}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testToJsonObjectCollector(final TestResult result) {
+    System.out.println(" - Collector returned by toJsonObject(Function,Function)");
+    final JsonArray in = Json.createArrayBuilder()
+        .add(Json.createObjectBuilder().add("key", STR_NAME).add("value",
+            STR_VALUE))
+        .add(Json.createObjectBuilder().add("key", INT_NAME).add("value",
+            INT_VALUE))
+        .add(Json.createObjectBuilder().add("key", BOOL_NAME).add("value",
+            BOOL_VALUE))
+        .add(Json.createObjectBuilder().add("key", OBJ_NAME).add("value",
+            OBJ_VALUE))
+        .build();
+    final JsonObject check = Json.createObjectBuilder().add(STR_NAME, STR_VALUE)
+        .add(INT_NAME, INT_VALUE).add(BOOL_NAME, BOOL_VALUE)
+        .add(OBJ_NAME, OBJ_VALUE).build();
+    System.out.println("     Input: " + valueToString(in));
+    final Collector<JsonValue, JsonObjectBuilder, JsonObject> col = JsonCollectors
+        .toJsonObject(
+            // Build key from stream value.
+            (JsonValue v) -> {
+              if (v.getValueType() == JsonValue.ValueType.OBJECT)
+                return v.asJsonObject().getString("key");
+              throw new IllegalStateException("Value must be JsonObject");
+            },
+            // Build value from stream value.
+            (JsonValue v) -> {
+              if (v.getValueType() == JsonValue.ValueType.OBJECT)
+                return v.asJsonObject().get("value");
+              throw new IllegalStateException("Value must be JsonObject");
+            });
+    final JsonObject out = in.getValuesAs(JsonObject.class).stream()
+        .collect(col);
+    if (operationFailed(out, check)) {
+      result.fail("toJsonObject(Function,Function)", "Output Stream value "
+          + valueToString(out) + " shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test collector returned by {@code groupingBy(Function<JsonValue,String>)}
+   * method. This collector allows grouping of {@code Stream<JsonValue>} using
+   * provided {@code Function<JsonValue,String>)} function to define group
+   * identifiers based on {@code JsonValue} content. Test just groups JSON
+   * objects in the stream by {@code "office"} attribute. Default
+   * {@code toJsonArray()} collector is used so output is unsorted.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testSimpleGroupingByCollector(final TestResult result) {
+    System.out.println(" - Collector returned by groupingBy(Function)");
+    final JsonObject check = Json.createObjectBuilder().add("Red", Json
+        .createArrayBuilder()
+        .add(
+            Json.createObjectBuilder().add("name", "John").add("office", "Red"))
+        .add(Json.createObjectBuilder().add("name", "Sarah").add("office",
+            "Red"))
+        .add(
+            Json.createObjectBuilder().add("name", "Rick").add("office", "Red"))
+        .build())
+        .add("Blue",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("name", "Bob").add("office",
+                    "Blue"))
+                .add(Json.createObjectBuilder().add("name", "Tom").add("office",
+                    "Blue"))
+                .add(Json.createObjectBuilder().add("name", "Jane")
+                    .add("office", "Blue"))
+                .build())
+        .add("Green",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("name", "Peter")
+                    .add("office", "Green"))
+                .add(Json.createObjectBuilder().add("name", "Peggy")
+                    .add("office", "Green"))
+                .build())
+        .build();
+    System.out.println("     Input: " + valueToString(OBJ_ARRAY_GROUP));
+    final Collector<JsonValue, Map<String, JsonArrayBuilder>, JsonObject> col = JsonCollectors
+        .groupingBy((JsonValue v) -> {
+          if (v.getValueType() == JsonValue.ValueType.OBJECT)
+            return v.asJsonObject().getString("office");
+          throw new IllegalStateException("Value must be JsonObject");
+        });
+    final JsonObject out = OBJ_ARRAY_GROUP.getValuesAs(JsonObject.class)
+        .stream().collect(col);
+    if (operationFailed(out, check)) {
+      result.fail("groupingBy(Function)", "Output Stream value "
+          + valueToString(out) + " shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Builder to create sorted JsonArray with ordering by {@code "name"}
+   * attribute of {@code JsonObject}.
+   */
+  private class ValueBuilder implements JsonArrayBuilder {
+
+    /** Sorted collection of values. */
+    private final TreeSet<JsonValue> values;
+
+    private ValueBuilder() {
+      values = new TreeSet<>((JsonValue v1, JsonValue v2) -> {
+        if (v1.getValueType() == JsonValue.ValueType.OBJECT
+            && v1.getValueType() == JsonValue.ValueType.OBJECT) {
+          return v1.asJsonObject().getString("name")
+              .compareTo(v2.asJsonObject().getString("name"));
+        }
+        throw new IllegalStateException("Values must be JsonObject");
+      });
+    }
+
+    /**
+     * Builder accumulator method.
+     * 
+     * @param value
+     *          Value to be added to {@code JsonArray}.
+     * @return This builder instance.
+     */
+    @Override
+    public JsonArrayBuilder add(JsonValue value) {
+      values.add(value);
+      return this;
+    }
+
+    /**
+     * Builder combiner method.
+     * 
+     * @param builder
+     *          Builder containing values to be added to {@code JsonArray}.
+     * @return This builder instance.
+     */
+    public ValueBuilder addAll(ValueBuilder builder) {
+      values.addAll(builder.values);
+      return this;
+    }
+
+    /**
+     * Builder finisher method.
+     * 
+     * @return {@code JsonArray} from current builder content.
+     */
+    @Override
+    public JsonArray build() {
+      JsonArrayBuilder builder = Json.createArrayBuilder();
+      for (JsonValue value : values) {
+        builder.add(value);
+      }
+      return builder.build();
+    }
+
+    @Override
+    public JsonArrayBuilder add(String value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(BigDecimal value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(BigInteger value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(int value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(long value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(double value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(boolean value) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder addNull() {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(JsonObjectBuilder builder) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+    @Override
+    public JsonArrayBuilder add(JsonArrayBuilder builder) {
+      throw new UnsupportedOperationException("Not supported yet."); // To
+                                                                     // change
+                                                                     // body of
+                                                                     // generated
+                                                                     // methods,
+                                                                     // choose
+                                                                     // Tools |
+                                                                     // Templates.
+    }
+
+  }
+
+  /**
+   * Test collector returned by
+   * {@code groupingBy(Function<JsonValue,String>,Collector<JsonValue,JsonArrayBuilder,JsonArray>)}
+   * method. This collector allows grouping of {@code Stream<JsonValue>} using
+   * provided {@code Function<JsonValue,String>)} function to define group
+   * identifiers based on {@code JsonValue} content and
+   * {@code Collector<JsonValue,JsonArrayBuilder,JsonArray>)}. Test groups JSON
+   * objects in the stream by {@code "office"} attribute. External collector is
+   * building sorted {@code JsonArray} so arrays for each office group are
+   * sorted.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testSortingGroupingByCollector(final TestResult result) {
+    System.out.println(" - Collector returned by groupingBy(Function,Collector)");
+    final JsonObject check = Json.createObjectBuilder().add("Red", Json
+        .createArrayBuilder()
+        .add(
+            Json.createObjectBuilder().add("name", "John").add("office", "Red"))
+        .add(
+            Json.createObjectBuilder().add("name", "Rick").add("office", "Red"))
+        .add(Json.createObjectBuilder().add("name", "Sarah").add("office",
+            "Red"))
+        .build())
+        .add("Blue",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("name", "Bob").add("office",
+                    "Blue"))
+                .add(Json.createObjectBuilder().add("name", "Jane")
+                    .add("office", "Blue"))
+                .add(Json.createObjectBuilder().add("name", "Tom").add("office",
+                    "Blue"))
+                .build())
+        .add("Green",
+            Json.createArrayBuilder()
+                .add(Json.createObjectBuilder().add("name", "Peggy")
+                    .add("office", "Green"))
+                .add(Json.createObjectBuilder().add("name", "Peter")
+                    .add("office", "Green"))
+                .build())
+        .build();
+    Collector<JsonValue, JsonArrayBuilder, JsonArray> toArray = Collector.of(
+        ValueBuilder::new, JsonArrayBuilder::add, JsonArrayBuilder::addAll,
+        JsonArrayBuilder::build);
+    System.out.println("     Input: " + valueToString(OBJ_ARRAY_GROUP));
+    final Collector<JsonValue, Map<String, JsonArrayBuilder>, JsonObject> col = JsonCollectors
+        .groupingBy((JsonValue v) -> {
+          if (v.getValueType() == JsonValue.ValueType.OBJECT)
+            return v.asJsonObject().getString("office");
+          throw new IllegalStateException("Value must be JsonObject");
+        }, toArray);
+    final JsonObject out = OBJ_ARRAY_GROUP.getValuesAs(JsonObject.class)
+        .stream().collect(col);
+    if (operationFailed(out, check)) {
+      result.fail("groupingBy(Function,Collector)", "Output Stream value "
+          + valueToString(out) + " shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    System.out.println("     Checking " + valueToString(out));
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ArrayBuilder.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ArrayBuilder.java
new file mode 100644
index 0000000..63abcfe
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ArrayBuilder.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonValue;
+
+// $Id$
+/**
+ * {@link JsonArrayBuilder} manipulation helper.
+ */
+public class ArrayBuilder {
+
+  /**
+   * Add {@code value} to provided JSON array builder.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param value
+   *          Value to be added at the end of the array.
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder add(final JsonArrayBuilder builder,
+      final Object value) {
+    switch (JsonValueType.getType(value)) {
+    case String:
+      return builder.add((String) value);
+    case Integer:
+      return builder.add(((Integer) value).intValue());
+    case Long:
+      return builder.add(((Long) value).intValue());
+    case BigInteger:
+      return builder.add(((BigInteger) value));
+    case Double:
+      return builder.add(((Double) value).doubleValue());
+    case BigDecimal:
+      return builder.add(((BigDecimal) value));
+    case Boolean:
+      return builder.add(((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.add((JsonValue) value);
+    case Null:
+      return builder.addNull();
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Add {@code value} at specified index to provided JSON array builder.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param index
+   *          Array index of value to be added.
+   * @param value
+   *          Value to be added at the end of the array.
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder add(final JsonArrayBuilder builder,
+      final int index, final Object value) {
+    switch (JsonValueType.getType(value)) {
+    case String:
+      return builder.add(index, (String) value);
+    case Integer:
+      return builder.add(index, ((Integer) value).intValue());
+    case Long:
+      return builder.add(index, ((Long) value).longValue());
+    case BigInteger:
+      return builder.add(index, ((BigInteger) value));
+    case Double:
+      return builder.add(index, ((Double) value).doubleValue());
+    case BigDecimal:
+      return builder.add(index, ((BigDecimal) value));
+    case Boolean:
+      return builder.add(index, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.add(index, (JsonValue) value);
+    case Null:
+      return builder.addNull(index);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Add {@code null} to provided JSON array builder. Every call shall throw an
+   * exception which depends on selected type.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param type
+   *          Type of method argument to use..
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder add(final JsonArrayBuilder builder,
+      final JsonValueType type) {
+    switch (type) {
+    case String:
+      return builder.add((String) null);
+    case Integer:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for int");
+    case Long:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for long");
+    case BigInteger:
+      return builder.add((BigInteger) null);
+    case Double:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for double");
+    case BigDecimal:
+      return builder.add((BigDecimal) null);
+    case Boolean:
+      return builder.add((Boolean) null);
+    case JsonValue:
+      return builder.add((JsonValue) null);
+    case Null:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for addNull()");
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Add {@code null} to provided JSON array builder. Every call shall throw an
+   * exception which depends on selected type.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param index
+   *          Array index of value to be added.
+   * @param type
+   *          Type of method argument to use..
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder add(final JsonArrayBuilder builder,
+      final int index, final JsonValueType type) {
+    switch (type) {
+    case String:
+      return builder.add(index, (String) null);
+    case Integer:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for int");
+    case Long:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for long");
+    case BigInteger:
+      return builder.add(index, (BigInteger) null);
+    case Double:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for double");
+    case BigDecimal:
+      return builder.add(index, (BigDecimal) null);
+    case Boolean:
+      return builder.add(index, (Boolean) null);
+    case JsonValue:
+      return builder.add(index, (JsonValue) null);
+    case Null:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for addNull()");
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Set {@code value} at specified index to provided JSON array builder.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param index
+   *          Array index of value to be added.
+   * @param value
+   *          Value to be set at the end of the array.
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder set(final JsonArrayBuilder builder,
+      final int index, final Object value) {
+    switch (JsonValueType.getType(value)) {
+    case String:
+      return builder.set(index, (String) value);
+    case Integer:
+      return builder.set(index, ((Integer) value).intValue());
+    case Long:
+      return builder.set(index, ((Long) value).longValue());
+    case BigInteger:
+      return builder.set(index, ((BigInteger) value));
+    case Double:
+      return builder.set(index, ((Double) value).doubleValue());
+    case BigDecimal:
+      return builder.set(index, ((BigDecimal) value));
+    case Boolean:
+      return builder.set(index, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.set(index, (JsonValue) value);
+    case Null:
+      return builder.setNull(index);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Set {@code null} to provided JSON array builder. Every call shall throw an
+   * exception which depends on selected type.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param index
+   *          Array index of value to be added.
+   * @param type
+   *          Type of method argument to use..
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder set(final JsonArrayBuilder builder,
+      final int index, final JsonValueType type) {
+    switch (type) {
+    case String:
+      return builder.set(index, (String) null);
+    case Integer:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for int");
+    case Long:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for long");
+    case BigInteger:
+      return builder.set(index, (BigInteger) null);
+    case Double:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for double");
+    case BigDecimal:
+      return builder.set(index, (BigDecimal) null);
+    case Boolean:
+      return builder.set(index, (Boolean) null);
+    case JsonValue:
+      return builder.set(index, (JsonValue) null);
+    case Null:
+      throw new UnsupportedOperationException(
+          "Value null is not supported for addNull()");
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Remove {@code value} at specified index from provided JSON array builder.
+   * 
+   * @param builder
+   *          Target JSON array builder.
+   * @param index
+   *          Array index of value to be added.
+   * @return JSON array builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonArrayBuilder remove(final JsonArrayBuilder builder,
+      final int index) {
+    return builder.remove(index);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonAssert.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonAssert.java
new file mode 100644
index 0000000..6afd391
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonAssert.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.common.JSONP_Util.toStringJsonArray;
+import static jakarta.jsonp.tck.common.JSONP_Util.toStringJsonObject;
+
+// $Id$
+/**
+ * JSON values assertions.
+ */
+public class JsonAssert {
+
+  private static boolean assertEquals(final JsonObject expected,
+      final JsonObject actual, final String message) {
+    if (actual.equals(expected)) {
+      return true;
+    } else {
+      System.out.println("   " + message);
+      System.out.println("     Expected: " + toStringJsonObject(expected));
+      System.out.println("     Actual:   " + toStringJsonObject(actual));
+      return false;
+    }
+  }
+
+  private static boolean assertEquals(final JsonArray expected,
+      final JsonArray actual, final String message) {
+    if (actual.equals(expected)) {
+      return true;
+    } else {
+      System.out.println("   " + message);
+      System.out.println("     Expected: " + toStringJsonArray(expected));
+      System.out.println("     Actual:   " + toStringJsonArray(actual));
+      return false;
+    }
+  }
+
+  private static boolean assertEquals(final JsonString expected,
+      final JsonString actual, final String message) {
+    if (actual.equals(expected)) {
+      return true;
+    } else {
+      System.out.println("   " + message);
+      System.out.println("     Expected: " + expected.getString());
+      System.out.println("     Actual:   " + actual.getString());
+      return false;
+    }
+  }
+
+  private static boolean assertEquals(final JsonNumber expected,
+      final JsonNumber actual, final String message) {
+    if (actual.equals(expected)) {
+      return true;
+    } else {
+      System.out.println("   " + message);
+      System.out.println("     Expected: " + expected.toString());
+      System.out.println("     Actual:   " + actual.toString());
+      return false;
+    }
+  }
+
+  public static boolean assertEquals(final JsonValue expected,
+      final JsonValue actual, final String message) {
+    switch (expected.getValueType()) {
+    case OBJECT:
+      return assertEquals((JsonObject) expected, (JsonObject) actual, message);
+    case ARRAY:
+      return assertEquals((JsonArray) expected, (JsonArray) actual, message);
+    case STRING:
+      return assertEquals((JsonString) expected, (JsonString) actual, message);
+    case NUMBER:
+      return assertEquals((JsonNumber) expected, (JsonNumber) actual, message);
+    case TRUE:
+    case FALSE:
+      if (expected == actual) {
+        return true;
+      } else {
+        System.out.println("   " + message);
+        System.out.println("     Expected: " + expected.toString());
+        System.out.println("     Actual:   " + actual.toString());
+        return false;
+      }
+    default:
+      if (actual.equals(expected)) {
+        return true;
+      } else {
+        System.out.println("   " + message);
+        System.out.println("     Expected: " + expected.toString());
+        System.out.println("     Actual:   " + actual.toString());
+        return false;
+      }
+    }
+  }
+
+  public static boolean assertEquals(final JsonValue expected,
+      final JsonValue actual) {
+    return assertEquals(expected, actual, "JSON mismatch");
+  }
+
+  /**
+   * Operation result expected.
+   * 
+   * @param expected
+   *          Expected modified JSON value.
+   * @param actual
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  public static boolean assertEquals(final Object expected,
+      final String actual) {
+    if (actual == null) {
+      return true;
+    }
+    try {
+      switch (JsonValueType.getType(expected)) {
+      case String:
+        String exp = '\"' + (String) expected + '\"';
+        return exp.equals(actual);
+      case Integer:
+        return Integer.parseInt(actual) == (Integer) expected;
+      case Long:
+        return Long.parseLong(actual) == (Long) expected;
+      case BigInteger:
+        return (new BigInteger(actual)).equals(expected);
+      case Double:
+        return Double.parseDouble(actual) == (Double) expected;
+      case BigDecimal:
+        return (new BigDecimal(actual)).equals(expected);
+      case Boolean:
+        return Boolean.parseBoolean(actual) == (Boolean) expected;
+      case JsonValue:
+        try (final JsonReader reader = Json
+            .createReader(new StringReader(actual))) {
+          final JsonValue actVal = reader.readValue();
+          return assertEquals((JsonValue) expected, actVal);
+        }
+      case Null:
+        try (final JsonReader reader = Json
+            .createReader(new StringReader(actual))) {
+          final JsonValue actVal = reader.readValue();
+          return assertEquals(JsonValue.NULL, actVal);
+        }
+      default:
+        throw new IllegalArgumentException(
+            "Value does not match known JSON value type");
+      }
+    } catch (NumberFormatException ex) {
+      return true;
+    }
+  }
+
+  /**
+   * Convert provided JSON value to human readable String.
+   * 
+   * @param value
+   *          Value to be converted.
+   * @return JSON value as human readable String.
+   */
+  public static String valueToString(final JsonValue value) {
+    switch (value.getValueType()) {
+    case OBJECT:
+      return toStringJsonObject((JsonObject) value);
+    case ARRAY:
+      return toStringJsonArray((JsonArray) value);
+    case STRING:
+      return ((JsonString) value).getString();
+    case NUMBER:
+      return ((JsonNumber) value).toString();
+    case TRUE:
+      return Boolean.toString(true);
+    case FALSE:
+      return Boolean.toString(false);
+    case NULL:
+      return "null";
+    default:
+      throw new IllegalArgumentException("Unknown value type");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonIO.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonIO.java
new file mode 100644
index 0000000..1261a5e
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonIO.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.io.StringReader;
+import jakarta.json.Json;
+import jakarta.json.JsonValue;
+
+// $Id$
+/**
+ * Read and write JSON values.
+ */
+public class JsonIO {
+  /**
+   * Reads JSON value from {@code String}.
+   * 
+   * @param json
+   *          JSON value to be read.
+   * @return JSON value from provided {@code String}.
+   */
+  public static JsonValue read(final String json) {
+    return Json.createReader(new StringReader(json)).readValue();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonValueType.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonValueType.java
new file mode 100644
index 0000000..f8da982
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/JsonValueType.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import jakarta.json.JsonValue;
+
+// $Id$
+/**
+ * Identifiers of types used as JSON value.
+ */
+public enum JsonValueType {
+  /** JsonValue is String. */
+  String,
+  /** JsonValue is Integer. */
+  Integer,
+  /** JsonValue is Long. */
+  Long,
+  /** JsonValue is BigInteger. */
+  BigInteger,
+  /** JsonValue is Double. */
+  Double,
+  /** JsonValue is BigDecimal. */
+  BigDecimal,
+  /** JsonValue is Boolean. */
+  Boolean,
+  /** JsonValue is common JSON value. */
+  JsonValue,
+  /** JsonValue is null. */
+  Null;
+
+  /** Size of this enumeration. */
+  private static final int SIZE = JsonValueType.values().length;
+
+  /** Name to value {@code Map}. */
+  private static final Map<String, JsonValueType> VALUES = new HashMap<>(SIZE);
+  // Name to value Map initialization.
+  static {
+    for (int i = 0; i < SIZE; i++)
+      VALUES.put(JsonValueType.values()[i].name(), JsonValueType.values()[i]);
+  }
+
+  /**
+   * Returns JSON value identifier for provided class.
+   * 
+   * @param c
+   *          JSON value class.
+   * @return JSON value identifier for provided class.
+   */
+  public static JsonValueType getType(final Class c) {
+    JsonValueType type = VALUES.get(c.getSimpleName());
+    if (type != null) {
+      return type;
+    }
+    // Interface hierarchy is a tree so stack machine is required to go trough
+    // it.
+    final LinkedList<Class> stack = new LinkedList();
+    for (final Class i : c.getInterfaces()) {
+      stack.push(i);
+    }
+    while (!stack.isEmpty()) {
+      final Class i = stack.pop();
+      type = VALUES.get(i.getSimpleName());
+      if (type != null) {
+        return type;
+      }
+      for (final Class j : i.getInterfaces()) {
+        stack.push(j);
+      }
+    }
+    throw new IllegalArgumentException(
+        "Unsupported JSON value type: " + c.getSimpleName());
+  }
+
+  /**
+   * Returns JSON value identifier for provided value.
+   * 
+   * @param value
+   *          JSON value.
+   * @return JSON value identifier for provided class.
+   */
+  public static JsonValueType getType(final Object value) {
+    return value != null ? getType(value.getClass()) : Null;
+  }
+
+  /**
+   * Convert provided value to {@code String} which is part of JSON document.
+   * 
+   * @param value
+   *          Value be be converted to {@code String}.
+   * @return Value converted to {@code String}.
+   */
+  public static String toStringValue(final Object value) {
+    switch (getType(value)) {
+    case String:
+      return '"' + ((String) value) + '"';
+    case Integer:
+      return ((Integer) value).toString();
+    case Long:
+      return ((Long) value).toString();
+    case BigInteger:
+      return ((BigInteger) value).toString();
+    case Double:
+      return ((Double) value).toString();
+    case BigDecimal:
+      return ((BigDecimal) value).toString();
+    case Boolean:
+      return ((Boolean) value).toString();
+    case JsonValue:
+      return JsonAssert.valueToString((JsonValue) value);
+    case Null:
+      return SimpleValues.NULL;
+    default:
+      throw new IllegalArgumentException(
+          "Unsupported JSON value type: " + value.getClass().getSimpleName());
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/MergeRFCObject.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/MergeRFCObject.java
new file mode 100644
index 0000000..54b6279
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/MergeRFCObject.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+
+/*
+ * $Id$
+ */
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc7396">RFC 7396</a>}: JavaScript
+ * Object Notation (JSON) Merge Patch compatibility sample object.<br>
+ * Object structure is defined in
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC 7396: 3.
+ * Example</a>}.
+ */
+public class MergeRFCObject {
+
+  // Following values define JSON object keys from RFC 7396: 3. Example
+  /** RFC 7396 sample JSON object key for {@code /title}. */
+  public static final String KEY_TITLE = "title";
+
+  /** RFC 7396 sample JSON object key for {@code /author/givenName}. */
+  public static final String KEY_GIVEN_NAME = "givenName";
+
+  /** RFC 7396 sample JSON object key for {@code /author/familyName}. */
+  public static final String KEY_FAMILY_NAME = "familyName";
+
+  /** RFC 7396 sample JSON object key for {@code /author}. */
+  public static final String KEY_AUTHOR = "author";
+
+  /** RFC 7396 sample JSON object key for {@code /tags}. */
+  public static final String KEY_TAGS = "tags";
+
+  /** RFC 7396 sample JSON object key for {@code /content}. */
+  public static final String KEY_CONTENT = "content";
+
+  /** RFC 7396 sample JSON object key for {@code /phoneNumber}. */
+  public static final String KEY_PHONE_NUMBER = "phoneNumber";
+
+  // Following values define JSON object source values from RFC 7396: 3. Example
+  /** RFC 7396 sample JSON object source value for {@code /title}. */
+  public static final String VAL_SRC_TITLE = "Goodbye!";
+
+  /** RFC 7396 sample JSON object source value for {@code /author/givenName}. */
+  public static final String VAL_SRC_GIVEN_NAME = "John";
+
+  /**
+   * RFC 7396 sample JSON object source value for {@code /author/familyName}.
+   */
+  public static final String VAL_SRC_FAMILY_NAME = "Doe";
+
+  /** RFC 7396 sample JSON object source value for {@code /author}. */
+  public static final JsonObject VAL_SRC_AUTHOR = SimpleValues
+      .createSimpleObject(new String[] { KEY_GIVEN_NAME, KEY_FAMILY_NAME },
+          new Object[] { VAL_SRC_GIVEN_NAME, VAL_SRC_FAMILY_NAME });
+
+  /** RFC 7396 sample JSON object source value for {@code /tags/0}. */
+  public static final String VAL_SRC_TAGS_0 = "example";
+
+  /** RFC 7396 sample JSON object source value for {@code /tags/1}. */
+  public static final String VAL_SRC_TAGS_1 = "sample";
+
+  /** RFC 7396 sample JSON object source value for {@code /tags}. */
+  public static final JsonArray VAL_SRC_TAGS = SimpleValues
+      .createStringArray(new String[] { VAL_SRC_TAGS_0, VAL_SRC_TAGS_1 });
+
+  /** RFC 7396 sample JSON object source value for {@code /content}. */
+  public static final String VAL_SRC_CONTENT = "This will be unchanged";
+
+  // Following values define JSON object target values from RFC 7396: 3. Example
+  /** RFC 7396 sample JSON object target value for {@code /title}. */
+  public static final String VAL_TRG_TITLE = "Hello!";
+
+  /** RFC 7396 sample JSON object target value for {@code /author}. */
+  public static final JsonObject VAL_TRG_AUTHOR = SimpleValues
+      .createSimpleObject(new String[] { KEY_GIVEN_NAME },
+          new Object[] { VAL_SRC_GIVEN_NAME });
+
+  /** RFC 7396 sample JSON object target value for {@code /phoneNumber}. */
+  public static final String VAL_TRG_PHONE_NUMBER = "+01-123-456-7890";
+
+  /** RFC 7396 sample JSON object target value for {@code /tags/0}. */
+  public static final String VAL_TRG_TAGS_0 = "example";
+
+  /** RFC 7396 sample JSON object target value for {@code /tags}. */
+  public static final JsonArray VAL_TRG_TAGS = SimpleValues
+      .createStringArray(new String[] { VAL_TRG_TAGS_0 });
+
+  // Following values define JSON object patch values from RFC 7396: 3. Example
+  /** RFC 7396 sample JSON object patch value for {@code /author/familyName}. */
+  public static final JsonValue VAL_PATCH_FAMILY_NAME = JsonValue.NULL;
+
+  /** RFC 7396 sample JSON object patch value for {@code /author}. */
+  public static final JsonObject VAL_PATCH_AUTHOR = SimpleValues
+      .createSimpleObject(new String[] { KEY_FAMILY_NAME },
+          new Object[] { VAL_PATCH_FAMILY_NAME });
+
+  /**
+   * Create {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC
+   * 7396 example</a>} source JSON object.
+   * 
+   * @return Source object from example.
+   */
+  public static JsonObject createRFCSourceObject() {
+    return Json.createObjectBuilder().add(KEY_TITLE, VAL_SRC_TITLE)
+        .add(KEY_AUTHOR, VAL_SRC_AUTHOR).add(KEY_TAGS, VAL_SRC_TAGS)
+        .add(KEY_CONTENT, VAL_SRC_CONTENT).build();
+  }
+
+  /**
+   * Create {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC
+   * 7396 example</a>} target JSON object.
+   * 
+   * @return Target object from example.
+   */
+  public static JsonObject createRFCTargetObject() {
+    return Json.createObjectBuilder().add(KEY_TITLE, VAL_TRG_TITLE)
+        .add(KEY_AUTHOR, VAL_TRG_AUTHOR).add(KEY_TAGS, VAL_TRG_TAGS)
+        .add(KEY_CONTENT, VAL_SRC_CONTENT)
+        .add(KEY_PHONE_NUMBER, VAL_TRG_PHONE_NUMBER).build();
+  }
+
+  /**
+   * Create {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC
+   * 7396 example</a>} patch JSON object.
+   * 
+   * @return Patch object from example.
+   */
+  public static JsonObject createRFCPatchObject() {
+    return Json.createObjectBuilder().add(KEY_TITLE, VAL_TRG_TITLE)
+        .add(KEY_PHONE_NUMBER, VAL_TRG_PHONE_NUMBER)
+        .add(KEY_AUTHOR, VAL_PATCH_AUTHOR).add(KEY_TAGS, VAL_TRG_TAGS).build();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ObjectBuilder.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ObjectBuilder.java
new file mode 100644
index 0000000..e31f468
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/ObjectBuilder.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+// $Id$
+/**
+ * {@link JsonObjectBuilder} manipulation helper.
+ */
+public class ObjectBuilder {
+
+  /**
+   * Add {@code value} with {@code name} to provided JSON object builder.
+   * 
+   * @param builder
+   *          Target JSON object builder.
+   * @param name
+   *          Name of value to be added.
+   * @param value
+   *          Value to be added.
+   * @return JSON object builder containing new {@code value}.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonObjectBuilder add(final JsonObjectBuilder builder,
+      final String name, final Object value) {
+    switch (JsonValueType.getType(value)) {
+    case String:
+      return builder.add(name, (String) value);
+    case Integer:
+      return builder.add(name, ((Integer) value).intValue());
+    case Long:
+      return builder.add(name, ((Long) value).intValue());
+    case BigInteger:
+      return builder.add(name, ((BigInteger) value));
+    case Double:
+      return builder.add(name, ((Double) value).doubleValue());
+    case BigDecimal:
+      return builder.add(name, ((BigDecimal) value));
+    case Boolean:
+      return builder.add(name, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.add(name, (JsonValue) value);
+    case Null:
+      return builder.addNull(name);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/PointerRFCObject.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/PointerRFCObject.java
new file mode 100644
index 0000000..b648d3c
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/PointerRFCObject.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+
+/*
+ * $Id$
+ */
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: JavaScript
+ * Object Notation (JSON) Pointer compatibility sample object.<br>
+ * Object structure is defined in
+ * {@see <a href="https://tools.ietf.org/html/rfc6901#section-5">RFC 6901: 5.
+ * JSON String Representation</a>}.
+ */
+public class PointerRFCObject {
+
+  // Following values define JSON object from
+  // RFC 6901: 5. JSON String Representation
+  // https://tools.ietf.org/html/rfc6901#section-5
+  /** RFC 6901 sample JSON object key for the whole document. */
+  public static final String RFC_KEY_WHOLE = "";
+
+  /** RFC 6901 sample JSON object key for 1st value. */
+  public static final String RFC_KEY1 = "foo";
+
+  /** RFC 6901 sample JSON object pointer for 1st value. */
+  public static final String RFC_PTR1 = "/foo";
+
+  /** RFC 6901 sample JSON object pointer for 1st item of 1st value. */
+  public static final String RFC_PTR1_ITEM1 = "/foo/0";
+
+  /** RFC 6901 sample JSON object pointer for 2nd item of 1st value. */
+  public static final String RFC_PTR1_ITEM2 = "/foo/1";
+
+  /** RFC 6901 sample JSON object 1st value: array 1st item. */
+  public static final String RFC_VAL1_ITEM1 = "bar";
+
+  /** RFC 6901 sample JSON object 1st value: array 2nd item. */
+  public static final String RFC_VAL1_ITEM2 = "baz";
+
+  /** RFC 6901 sample JSON object 1st value. */
+  public static final JsonArray RFC_VAL1 = SimpleValues
+      .createStringArray(RFC_VAL1_ITEM1, RFC_VAL1_ITEM2);
+
+  /** RFC 6901 sample JSON object key for 2nd value. */
+  public static final String RFC_KEY2 = "";
+
+  /** RFC 6901 sample JSON object pointer for 2nd value. */
+  public static final String RFC_PTR2 = "/";
+
+  /** RFC 6901 sample JSON object 2nd value. */
+  public static final int RFC_VAL2 = 0;
+
+  /** RFC 6901 sample JSON object key for 3rd value. */
+  public static final String RFC_KEY3 = "a/b";
+
+  /** RFC 6901 sample JSON object pointer for 3rd value. */
+  public static final String RFC_PTR3_ENC = "/a~1b";
+
+  /** RFC 6901 sample JSON object pointer for 3rd value. */
+  public static final String RFC_PTR3 = "/a/b";
+
+  /** RFC 6901 sample JSON object 3rd value. */
+  public static final int RFC_VAL3 = 1;
+
+  /** RFC 6901 sample JSON object key for 4th value. */
+  public static final String RFC_KEY4 = "c%d";
+
+  /** RFC 6901 sample JSON object pointer for 4th value. */
+  public static final String RFC_PTR4 = "/c%d";
+
+  /** RFC 6901 sample JSON object 4th value. */
+  public static final int RFC_VAL4 = 2;
+
+  /** RFC 6901 sample JSON object key for 5th value. */
+  public static final String RFC_KEY5 = "e^f";
+
+  /** RFC 6901 sample JSON object pointer for 5th value. */
+  public static final String RFC_PTR5 = "/e^f";
+
+  /** RFC 6901 sample JSON object 5th value. */
+  public static final int RFC_VAL5 = 3;
+
+  /** RFC 6901 sample JSON object key for 6th value. */
+  public static final String RFC_KEY6 = "g|h";
+
+  /** RFC 6901 sample JSON object pointer for 6th value. */
+  public static final String RFC_PTR6 = "/g|h";
+
+  /** RFC 6901 sample JSON object 6th value. */
+  public static final int RFC_VAL6 = 4;
+
+  /** RFC 6901 sample JSON object key for 7th value. */
+  public static final String RFC_KEY7 = "i\\j";
+
+  /** RFC 6901 sample JSON object pointer for 7th value. */
+  public static final String RFC_PTR7 = "/i\\j";
+
+  /** RFC 6901 sample JSON object 7th value. */
+  public static final int RFC_VAL7 = 5;
+
+  /** RFC 6901 sample JSON object key for 8th value. */
+  public static final String RFC_KEY8 = "k\"l";
+
+  /** RFC 6901 sample JSON object pointer for 8th value. */
+  public static final String RFC_PTR8 = "/k\"l";
+
+  /** RFC 6901 sample JSON object 8th value. */
+  public static final int RFC_VAL8 = 6;
+
+  /** RFC 6901 sample JSON object key for 9th value. */
+  public static final String RFC_KEY9 = " ";
+
+  /** RFC 6901 sample JSON object pointer for 9th value. */
+  public static final String RFC_PTR9 = "/ ";
+
+  /** RFC 6901 sample JSON object 9th value. */
+  public static final int RFC_VAL9 = 7;
+
+  /** RFC 6901 sample JSON object key for 10th value. */
+  public static final String RFC_KEY10 = "m~n";
+
+  /** RFC 6901 sample JSON object encoded pointer for 10th value. */
+  public static final String RFC_KEY10_ENC = "/m~0n";
+
+  /** RFC 6901 sample JSON object pointer for 10th value. */
+  public static final String RFC_PTR10 = "/m~n";
+
+  /** RFC 6901 sample JSON object 10th value. */
+  public static final int RFC_VAL10 = 8;
+
+  /** RFC 6901 sample JSON object key for 11th value. */
+  public static final String RFC_KEY11 = "o~1p";
+
+  /** RFC 6901 sample JSON object encoded pointer for 11th value. */
+  public static final String RFC_PTR11_ENC = "/o~01p";
+
+  /** RFC 6901 sample JSON object pointer for 11th value. */
+  public static final String RFC_PTR11 = "/o~1p";
+
+  /** RFC 6901 sample JSON object 11th value. */
+  public static final int RFC_VAL11 = 9;
+
+  /**
+   * Creates RFC 6901 sample JSON object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-5">RFC 6901: 5.
+   * JSON String Representation</a>}
+   * 
+   * @return RFC 6901 sample JSON object.
+   */
+  public static JsonObject createRFC6901Object() {
+    return Json.createObjectBuilder().add(RFC_KEY1, RFC_VAL1)
+        .add(RFC_KEY2, RFC_VAL2).add(RFC_KEY3, RFC_VAL3).add(RFC_KEY4, RFC_VAL4)
+        .add(RFC_KEY5, RFC_VAL5).add(RFC_KEY6, RFC_VAL6).add(RFC_KEY7, RFC_VAL7)
+        .add(RFC_KEY8, RFC_VAL8).add(RFC_KEY9, RFC_VAL9)
+        .add(RFC_KEY10, RFC_VAL10).add(RFC_KEY11, RFC_VAL11).build();
+  }
+
+  /**
+   * Create an instance of RFC 6901 object class is not allowed.
+   */
+  private PointerRFCObject() {
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/SimpleValues.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/SimpleValues.java
new file mode 100644
index 0000000..7ebe2b5
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/SimpleValues.java
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonWriter;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Build various simple values for testing.
+ */
+public class SimpleValues {
+
+  /** Name of JSON {@code String} value used in tests. */
+  public static final String STR_NAME = "address";
+
+  /** Path of JSON {@code String} value used in tests. */
+  public static final String STR_PATH = "/" + STR_NAME;
+
+  /** JSON {@code String} value used in tests. */
+  public static final String STR_VALUE = "In a galaxy far far away";
+
+  /** JSON {@code String} second value used in tests. */
+  public static final String STR_VALUE2 = "In a land of myth";
+
+  /** Name of JSON {@code int} value used in tests. */
+  public static final String INT_NAME = "age";
+
+  /** Path of JSON {@code int} value used in tests. */
+  public static final String INT_PATH = "/" + INT_NAME;
+
+  /** JSON {@code int} value used in tests. */
+  public static final int INT_VALUE = 42;
+
+  /** JSON {@code int} second value used in tests. */
+  public static final int INT_VALUE2 = 32;
+
+  /** Name of JSON {@code boolean} value used in tests. */
+  public static final String BOOL_NAME = "married";
+
+  /** Path of JSON {@code boolean} value used in tests. */
+  public static final String BOOL_PATH = "/" + BOOL_NAME;
+
+  /** JSON {@code boolean} value used in tests. */
+  public static final boolean BOOL_VALUE = true;
+
+  /** JSON {@code boolean} second value used in tests. */
+  public static final boolean BOOL_VALUE2 = false;
+
+  /** Name of JSON {@code JsonObject} value used in tests. */
+  public static final String OBJ_NAME = "wife";
+
+  /** Path of JSON {@code JsonObject} value used in tests. */
+  public static final String OBJ_PATH = "/" + OBJ_NAME;
+
+  /** JSON {@code JsonObject} value used in tests. */
+  public static final JsonObject OBJ_VALUE = createSimpleObject(
+      new String[] { "name", "age" }, new Object[] { "Sarah Connor", 32 });
+
+  /** JSON {@code JsonObject} second value used in tests. */
+  public static final JsonObject OBJ_VALUE2 = createSimpleObject(
+      new String[] { "name", "age" }, new Object[] { "Kyle Reese", 35 });
+
+  /** Name of JSON default value stored in simple object. */
+  public static final String DEF_NAME = "name";
+
+  /** Name of JSON default value stored in simple object. */
+  public static final String DEF_PATH = "/" + DEF_NAME;
+
+  /** JSON default value stored in simple object. */
+  public static final String DEF_VALUE = "John Smith";
+
+  /** Name of JSON object stored in compound object. */
+  public static final String DEF_OBJ_NAME = "child";
+
+  /** Path of JSON object stored in compound object. */
+  public static final String DEF_OBJ_PATH = "/child";
+
+  /** JSON default object stored in simple object. */
+  public static final JsonObject DEF_OBJ_VALUE = createSimpleObject(
+      new String[] { "name", "age" }, new Object[] { "John Connor", 6 });
+
+  /** JSON default object stored in simple object with name changed. */
+  public static final JsonObject DEF_OBJ_VALUE2 = createSimpleObject(
+      new String[] { "name", "age" }, new Object[] { "John Smith", 6 });
+
+  /** Value of JSON {@code String} array at index 0. */
+  public static final String STR_VALUE_1 = "First value";
+
+  /** Value of JSON {@code String} array at index 1. */
+  public static final String STR_VALUE_2 = "Second value";
+
+  /** Value of JSON {@code String} array at index 2. */
+  public static final String STR_VALUE_3 = "Third value";
+
+  /** Value of JSON {@code String} array at index 3. */
+  public static final String STR_VALUE_4 = "Fourth value";
+
+  /** Value of JSON {@code String} array at index 4. */
+  public static final String STR_VALUE_5 = "Fifth value";
+
+  /** Value of JSON {@code String} array at index 5. */
+  public static final String STR_VALUE_6 = "Sixth value";
+
+  /** Value of JSON {@code String} array at index 6. */
+  public static final String STR_VALUE_7 = "Seventh value";
+
+  /** Value of JSON {@code int} array at index 0. */
+  public static final int INT_VALUE_1 = 1;
+
+  /** Value of JSON {@code int} array at index 1. */
+  public static final int INT_VALUE_2 = 2;
+
+  /** Value of JSON {@code int} array at index 2. */
+  public static final int INT_VALUE_3 = 3;
+
+  /** Value of JSON {@code int} array at index 3. */
+  public static final int INT_VALUE_4 = 4;
+
+  /** Value of JSON {@code int} array at index 4. */
+  public static final int INT_VALUE_5 = 5;
+
+  /** Value of JSON {@code JsonObject} array at index 0. */
+  public static final JsonObject OBJ_VALUE_1 = createSimpleObject(
+      new String[] { "first" }, new String[] { STR_VALUE_1 });
+
+  /** Value of JSON {@code JsonObject} array at index 1. */
+  public static final JsonObject OBJ_VALUE_2 = createSimpleObject(
+      new String[] { "second" }, new String[] { STR_VALUE_2 });
+
+  /** Value of JSON {@code JsonObject} array at index 2. */
+  public static final JsonObject OBJ_VALUE_3 = createSimpleObject(
+      new String[] { "third" }, new String[] { STR_VALUE_3 });
+
+  /** Value of JSON {@code JsonObject} array at index 3. */
+  public static final JsonObject OBJ_VALUE_4 = createSimpleObject(
+      new String[] { "fourth" }, new String[] { STR_VALUE_4 });
+
+  /** Value of JSON {@code JsonObject} array at index 4. */
+  public static final JsonObject OBJ_VALUE_5 = createSimpleObject(
+      new String[] { "fifth" }, new String[] { STR_VALUE_5 });
+
+  /** JSON {@code boolean} value: {@code true}. */
+  public static final boolean BOOL_TRUE = true;
+
+  /** JSON {@code boolean} value: {@code false}. */
+  public static final boolean BOOL_FALSE = false;
+
+  /** JSON {@code long} value used in tests. */
+  public static final long LNG_VALUE = Long.MAX_VALUE - 42;
+
+  /** JSON {@code BigInteger} value used in tests. */
+  public static final BigInteger BIN_VALUE = new BigInteger(
+      "123456789012345678901234567890");
+
+  /** JSON {@code double} value used in tests. */
+  public static final double DBL_VALUE = 0x1.f5c926b3a0942P+1014;
+
+  /** JSON {@code BigDecimal} value used in tests. */
+  public static final BigDecimal BDC_VALUE = new BigDecimal(
+      new BigInteger("1234567890123456789012345678901234567890"), 10);
+
+  /** Message content: null String. */
+  public static final String NULL = "null";
+
+  /**
+   * Creates empty JSON object.
+   * 
+   * @return Empty JSON object.
+   */
+  public static JsonObject createEmptyObject() {
+    return Json.createObjectBuilder().build();
+  }
+
+  /**
+   * Creates empty JSON object after ADD STR_NAME STR_VALUE operation.
+   * 
+   * @return Empty JSON object after ADD STR_NAME STR_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectStr() {
+    return Json.createObjectBuilder().add(STR_NAME, STR_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON object after ADD INT_NAME INT_VALUE operation.
+   * 
+   * @return Empty JSON object after ADD INT_NAME INT_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectInt() {
+    return Json.createObjectBuilder().add(INT_NAME, INT_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON object after ADD BOOL_NAME BOOL_VALUE operation.
+   * 
+   * @return Empty JSON object after ADD BOOL_NAME BOOL_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectBool() {
+    return Json.createObjectBuilder().add(BOOL_NAME, BOOL_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   * 
+   * @return Empty JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectObject() {
+    return Json.createObjectBuilder().add(OBJ_NAME, OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates JSON patch to remove STR_NAME attribute from object.
+   * 
+   * @return JSON patch
+   */
+  public static JsonObject createPatchRemoveStr() {
+    return Json.createObjectBuilder().add(STR_NAME, JsonValue.NULL).build();
+  }
+
+  /**
+   * Creates JSON patch to remove INT_NAME attribute from object.
+   * 
+   * @return JSON patch
+   */
+  public static JsonObject createPatchRemoveInt() {
+    return Json.createObjectBuilder().add(INT_NAME, JsonValue.NULL).build();
+  }
+
+  /**
+   * Creates JSON patch to remove BOOL_NAME attribute from object.
+   * 
+   * @return JSON patch
+   */
+  public static JsonObject createPatchRemoveBool() {
+    return Json.createObjectBuilder().add(BOOL_NAME, JsonValue.NULL).build();
+  }
+
+  /**
+   * Creates JSON patch to remove OBJ_NAME attribute from object.
+   * 
+   * @return JSON patch
+   */
+  public static JsonObject createPatchRemoveObject() {
+    return Json.createObjectBuilder().add(OBJ_NAME, JsonValue.NULL).build();
+  }
+
+  /**
+   * Creates empty JSON array.
+   * 
+   * @return Empty JSON array.
+   */
+  public static JsonArray createEmptyArray() {
+    return Json.createArrayBuilder().build();
+  }
+
+  /**
+   * Creates empty JSON array after ADD STR_VALUE operation.
+   * 
+   * @return Empty JSON array after ADD STR_VALUE operation.
+   */
+  public static JsonArray createEmptyArrayWithStr() {
+    return Json.createArrayBuilder().add(STR_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON array after ADD INT_VALUE operation.
+   * 
+   * @return Empty JSON array after ADD INT_VALUE operation.
+   */
+  public static JsonArray createEmptyArrayWithInt() {
+    return Json.createArrayBuilder().add(INT_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON array after ADD BOOL_VALUE operation.
+   * 
+   * @return Empty JSON array after ADD BOOL_VALUE operation.
+   */
+  public static JsonArray createEmptyArrayWithBool() {
+    return Json.createArrayBuilder().add(BOOL_VALUE).build();
+  }
+
+  /**
+   * Creates empty JSON array after ADD OBJ_VALUE operation.
+   * 
+   * @return Empty JSON array after ADD OBJ_VALUE operation.
+   */
+  public static JsonArray createEmptyArrayWithObject() {
+    return Json.createArrayBuilder().add(OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object.
+   * 
+   * @return Simple JSON object.
+   */
+  public static JsonObject createSimpleObject() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after ADD STR_NAME STR_VALUE operation.
+   * 
+   * @return Simple JSON object after ADD STR_NAME STR_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectWithStr() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(STR_NAME, STR_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after REPLACE STR_NAME STR_VALUE operation.
+   * 
+   * @return Simple JSON object after REPLACE STR_NAME STR_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectReplaceStr() {
+    return Json.createObjectBuilder().add(STR_NAME, STR_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after MOVE STR_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after MOVE STR_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectMoveStr() {
+    return Json.createObjectBuilder().add(DEF_NAME, STR_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after COPY STR_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after COPY STR_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectCopyStr() {
+    return Json.createObjectBuilder().add(STR_NAME, STR_VALUE)
+        .add(DEF_NAME, STR_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after ADD INT_NAME INT_VALUE operation.
+   * 
+   * @return Simple JSON object after ADD INT_NAME INT_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectWithInt() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(INT_NAME, INT_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after REPLACE INT_NAME INT_VALUE operation.
+   * 
+   * @return Simple JSON object after REPLACE INT_NAME INT_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectReplaceInt() {
+    return Json.createObjectBuilder().add(INT_NAME, INT_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after MOVE INT_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after MOVE INT_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectMoveInt() {
+    return Json.createObjectBuilder().add(DEF_NAME, INT_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after COPY INT_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after COPY INT_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectCopyInt() {
+    return Json.createObjectBuilder().add(INT_NAME, INT_VALUE)
+        .add(DEF_NAME, INT_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after ADD BOOL_NAME BOOL_VALUE operation.
+   * 
+   * @return Simple JSON object after ADD BOOL_NAME BOOL_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectWithBool() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(BOOL_NAME, BOOL_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after REPLACE BOOL_NAME BOOL_VALUE operation.
+   * 
+   * @return Simple JSON object after REPLACE BOOL_NAME BOOL_VALUE operation.
+   */
+  public static JsonObject createSimpleObjectReplaceBool() {
+    return Json.createObjectBuilder().add(BOOL_NAME, BOOL_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after MOVE BOOL_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after MOVE BOOL_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectMoveBool() {
+    return Json.createObjectBuilder().add(DEF_NAME, BOOL_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after COPY BOOL_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after COPY BOOL_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectCopyBool() {
+    return Json.createObjectBuilder().add(BOOL_NAME, BOOL_VALUE)
+        .add(DEF_NAME, BOOL_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object.
+   * 
+   * @return Simple JSON object.
+   */
+  public static JsonObject createCompoundObject() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   * 
+   * @return Simple JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   */
+  public static JsonObject createCompoundObjectWithObject() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE).add(OBJ_NAME, OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   * 
+   * @return Simple JSON object after ADD OBJ_NAME OBJ_VALUE operation.
+   */
+  public static JsonObject createCompoundObjectReplaceObject() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE).add(OBJ_NAME, OBJ_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after MOVE DEF_PATH DEF_OBJ_PATH+DEF_PATH.
+   * 
+   * @return Simple JSON object.
+   */
+  public static JsonObject createCompoundObjectMoveValue() {
+    return Json.createObjectBuilder().add(DEF_OBJ_NAME, DEF_OBJ_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after COPY DEF_PATH DEF_OBJ_PATH+DEF_PATH.
+   * 
+   * @return Simple JSON object.
+   */
+  public static JsonObject createCompoundObjectCopyValue() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE2).build();
+  }
+
+  /**
+   * Creates simple JSON object after MOVE OBJ_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after MOVE OBJ_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectMoveObject() {
+    return Json.createObjectBuilder().add(DEF_NAME, OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON object after COPY OBJ_NAME DEF_NAME operation.
+   * 
+   * @return Simple JSON object after COPY OBJ_NAME DEF_NAME operation.
+   */
+  public static JsonObject createSimpleObjectCopyObject() {
+    return Json.createObjectBuilder().add(OBJ_NAME, OBJ_VALUE)
+        .add(DEF_NAME, OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates compound JSON object after ADD DEF_OBJ_NAME,
+   * createSimpleStringArray5().
+   * 
+   * @return compound JSON object with ADD operation applied.
+   */
+  public static JsonObject createCompoundObjectWithObjectReplaced() {
+    return Json.createObjectBuilder().add(DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, createSimpleStringArray5()).build();
+  }
+
+  /**
+   * Creates simple JSON object with provided {@code name[i]} and
+   * {@code value[i]} pairs.
+   * 
+   * @param names
+   *          Names of JSON values to be added. Pairs of {@code names[i]} and
+   *          {@code values[i]} are used for add operations.
+   * @param values
+   *          JSON values to be added for specified names.
+   * @return Simple JSON object.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonObject createSimpleObject(final String[] names,
+      final Object[] values) {
+    if (names.length != values.length) {
+      throw new IllegalArgumentException(
+          "Number of paths does not match number of indexes");
+    }
+    JsonObjectBuilder builder = Json.createObjectBuilder();
+    for (int i = 0; i < names.length; i++) {
+      switch (JsonValueType.getType(values[i].getClass())) {
+      case String:
+        builder = builder.add(names[i], (String) values[i]);
+        break;
+      case Integer:
+        builder = builder.add(names[i], ((Integer) values[i]).intValue());
+        break;
+      case Boolean:
+        builder = builder.add(names[i], ((Boolean) values[i]).booleanValue());
+        break;
+      case JsonValue:
+        builder = builder.add(names[i], (JsonValue) values[i]);
+        break;
+      default:
+        throw new IllegalArgumentException(
+            "Value does not match known JSON value type");
+      }
+    }
+    return builder.build();
+  }
+
+  /**
+   * Creates simple JSON array with specified {@code String} values.
+   * 
+   * @param values
+   *          JSON array {@code String} values.
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray(final String... values) {
+    JsonArrayBuilder builder = Json.createArrayBuilder();
+    if (values != null) {
+      for (final String value : values) {
+        builder = builder.add(value);
+      }
+    }
+    return builder.build();
+  }
+
+  /**
+   * Creates simple JSON array with single {@code String}.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray1() {
+    return Json.createArrayBuilder().add(STR_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD STR_VALUE operation before existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD STR_VALUE operation before existing
+   *         element.
+   */
+  public static JsonArray createSimpleStringArrayWithStrBefore() {
+    return Json.createArrayBuilder().add(STR_VALUE).add(STR_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD STR_VALUE operation after existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD STR_VALUE operation after existing
+   *         element.
+   */
+  public static JsonArray createSimpleStringArrayWithStrAfter() {
+    return Json.createArrayBuilder().add(STR_VALUE_1).add(STR_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array after REPLACE STR_VALUE operation on existing
+   * element.
+   * 
+   * @return Simple JSON array after REPLACE STR_VALUE operation on existing
+   *         element.
+   */
+  public static JsonArray createSimpleStringArrayReplaceStr() {
+    return Json.createArrayBuilder().add(STR_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code String} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray2() {
+    return Json.createArrayBuilder().add(STR_VALUE_2).add(STR_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code String} values in reversed order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray2R() {
+    return Json.createArrayBuilder().add(STR_VALUE_4).add(STR_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code String} values to be inserted
+   * into another array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringInnerArray2() {
+    return Json.createArrayBuilder().add(STR_VALUE_6).add(STR_VALUE_7).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code String} values as a result of
+   * COPY 2nd element to the beginning of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray2Copy1to0() {
+    return Json.createArrayBuilder().add(STR_VALUE_4).add(STR_VALUE_2)
+        .add(STR_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code String} values as a result of
+   * COPY 1st element to the end of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray2Copy0to2() {
+    return Json.createArrayBuilder().add(STR_VALUE_2).add(STR_VALUE_4)
+        .add(STR_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code String} values as a result of
+   * COPY 1st element to the middle of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createStringArray2Copy0to1() {
+    return Json.createArrayBuilder().add(STR_VALUE_2).add(STR_VALUE_2)
+        .add(STR_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code String} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleStringArray5() {
+    return Json.createArrayBuilder().add(STR_VALUE_1).add(STR_VALUE_2)
+        .add(STR_VALUE_3).add(STR_VALUE_4).add(STR_VALUE_5).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code String} values in reversed
+   * order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleStringArray5R() {
+    return Json.createArrayBuilder().add(STR_VALUE_5).add(STR_VALUE_4)
+        .add(STR_VALUE_3).add(STR_VALUE_2).add(STR_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD [STR_VALUE_6, STR_VALUE_7] operation
+   * between two existing elements.
+   * 
+   * @return Simple JSON array after ADD operation.
+   */
+  public static JsonArray createStringArray2WithStringArrayInTheMiddle() {
+    return Json.createArrayBuilder().add(STR_VALUE_2)
+        .add(createStringInnerArray2()).add(STR_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with single {@code int}.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray1() {
+    return Json.createArrayBuilder().add(INT_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD INT_VALUE operation before existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD INT_VALUE operation before existing
+   *         element.
+   */
+  public static JsonArray createSimpleIntArrayWithIntBefore() {
+    return Json.createArrayBuilder().add(INT_VALUE).add(INT_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD INT_VALUE operation after existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD INT_VALUE operation after existing
+   *         element.
+   */
+  public static JsonArray createSimpleIntArrayWithIntAfter() {
+    return Json.createArrayBuilder().add(INT_VALUE_1).add(INT_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array after REPLACE INT_VALUE operation on existing
+   * element.
+   * 
+   * @return Simple JSON array after REPLACE INT_VALUE operation on existing
+   *         element.
+   */
+  public static JsonArray createSimpleIntArrayReplaceInt() {
+    return Json.createArrayBuilder().add(INT_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code int} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray2() {
+    return Json.createArrayBuilder().add(INT_VALUE_2).add(INT_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code int} values in reversed order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray2R() {
+    return Json.createArrayBuilder().add(INT_VALUE_4).add(INT_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code int} values as a result of COPY
+   * 2nd element to the beginning of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray2Copy1to0() {
+    return Json.createArrayBuilder().add(INT_VALUE_4).add(INT_VALUE_2)
+        .add(INT_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code int} values as a result of COPY
+   * 1st element to the end of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray2Copy0to2() {
+    return Json.createArrayBuilder().add(INT_VALUE_2).add(INT_VALUE_4)
+        .add(INT_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code int} values as a result of COPY
+   * 1st element to the middle of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createIntArray2Copy0to1() {
+    return Json.createArrayBuilder().add(INT_VALUE_2).add(INT_VALUE_2)
+        .add(INT_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code int} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleIntArray5() {
+    return Json.createArrayBuilder().add(INT_VALUE_1).add(INT_VALUE_2)
+        .add(INT_VALUE_3).add(INT_VALUE_4).add(INT_VALUE_5).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code int} values in reversed order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleIntArray5R() {
+    return Json.createArrayBuilder().add(INT_VALUE_5).add(INT_VALUE_4)
+        .add(INT_VALUE_3).add(INT_VALUE_2).add(INT_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array with single {@code boolean}.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray1() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD BOOL_FALSE operation before existing
+   * element (BOOL_TRUE).
+   * 
+   * @return Simple JSON array after ADD BOOL_FALSE operation before existing
+   *         element.
+   */
+  public static JsonArray createSimpleBoolArrayWithBoolBefore() {
+    return Json.createArrayBuilder().add(BOOL_FALSE).add(BOOL_TRUE).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD BOOL_FALSE operation after existing
+   * element (BOOL_TRUE).
+   * 
+   * @return Simple JSON array after ADD BOOL_FALSE operation after existing
+   *         element.
+   */
+  public static JsonArray createSimpleBoolArrayWithBoolAfter() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array after REPLACE BOOL_FALSE operation on existing
+   * element.
+   * 
+   * @return Simple JSON array after REPLACE BOOL_FALSE operation on existing
+   *         element.
+   */
+  public static JsonArray createSimpleBoolArrayReplaceBool() {
+    return Json.createArrayBuilder().add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code boolean} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray2() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code boolean} values in reverse order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray2R() {
+    return Json.createArrayBuilder().add(BOOL_FALSE).add(BOOL_TRUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code boolean} values as a result of
+   * COPY 2nd element to the beginning of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray2Copy1to0() {
+    return Json.createArrayBuilder().add(BOOL_FALSE).add(BOOL_TRUE)
+        .add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code boolean} values as a result of
+   * COPY 1st element to the end of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray2Copy0to2() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).add(BOOL_FALSE)
+        .add(BOOL_TRUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code boolean} values as a result of
+   * COPY 1st element to the middle of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createBoolArray2Copy0to1() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).add(BOOL_TRUE)
+        .add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code boolean} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleBoolArray5() {
+    return Json.createArrayBuilder().add(BOOL_FALSE).add(BOOL_TRUE)
+        .add(BOOL_TRUE).add(BOOL_FALSE).add(BOOL_TRUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code boolean} values in reversed
+   * order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleBoolArray5R() {
+    return Json.createArrayBuilder().add(BOOL_TRUE).add(BOOL_FALSE)
+        .add(BOOL_TRUE).add(BOOL_TRUE).add(BOOL_FALSE).build();
+  }
+
+  /**
+   * Creates simple JSON array with single {@code JsonObject}.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray1() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD OBJ_VALUE operation before existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD OBJ_VALUE operation before existing
+   *         element.
+   */
+  public static JsonArray createSimpleObjectArrayWithObjectBefore() {
+    return Json.createArrayBuilder().add(OBJ_VALUE).add(OBJ_VALUE_1).build();
+  }
+
+  /**
+   * Creates simple JSON array after ADD OBJ_VALUE operation after existing
+   * element.
+   * 
+   * @return Simple JSON array after ADD OBJ_VALUE operation after existing
+   *         element.
+   */
+  public static JsonArray createSimpleObjectArrayWithObjectAfter() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_1).add(OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array after REPLACE OBJ_VALUE operation on existing
+   * element.
+   * 
+   * @return Simple JSON array after REPLACE OBJ_VALUE operation on existing
+   *         element.
+   */
+  public static JsonArray createSimpleObjectArrayReplaceObject() {
+    return Json.createArrayBuilder().add(OBJ_VALUE).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code JsonObject} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray2() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_2).add(OBJ_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with two {@code JsonObject} values in reverse
+   * order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray2R() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_4).add(OBJ_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code JsonObject} values as a result
+   * of COPY 2nd element to the beginning of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray2Copy1to0() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_4).add(OBJ_VALUE_2)
+        .add(OBJ_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code JsonObject} values as a result
+   * of COPY 1st element to the end of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray2Copy0to2() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_2).add(OBJ_VALUE_4)
+        .add(OBJ_VALUE_2).build();
+  }
+
+  /**
+   * Creates simple JSON array with three {@code JsonObject} values as a result
+   * of COPY 1st element to the middle of the array.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createObjectArray2Copy0to1() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_2).add(OBJ_VALUE_2)
+        .add(OBJ_VALUE_4).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code JsonObject} values.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleObjectArray5() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_1).add(OBJ_VALUE_2)
+        .add(OBJ_VALUE_3).add(OBJ_VALUE_4).add(OBJ_VALUE_5).build();
+  }
+
+  /**
+   * Creates simple JSON array with five {@code JsonObject} values in reversed
+   * order.
+   * 
+   * @return Newly created JSON array.
+   */
+  public static JsonArray createSimpleObjectArray5R() {
+    return Json.createArrayBuilder().add(OBJ_VALUE_5).add(OBJ_VALUE_4)
+        .add(OBJ_VALUE_3).add(OBJ_VALUE_2).add(OBJ_VALUE_1).build();
+  }
+
+  /**
+   * Convert provided {@code Object} instance to {@code JsonValue}.
+   * 
+   * @param value
+   *          {@code Object} instance to be converted.
+   * @return JsonValue instance containing provided {@code Object};
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  public static JsonValue toJsonValue(final Object value) {
+    if (value == null) {
+      return JsonValue.NULL;
+    }
+    switch (JsonValueType.getType(value.getClass())) {
+    case String:
+      return Json.createValue((String) value);
+    case Integer:
+      return Json.createValue(((Integer) value).intValue());
+    case Long:
+      return Json.createValue(((Long) value).longValue());
+    case BigInteger:
+      return Json.createValue((BigInteger) value);
+    case Double:
+      return Json.createValue(((Double) value).doubleValue());
+    case BigDecimal:
+      return Json.createValue((BigDecimal) value);
+    case Boolean:
+      return ((Boolean) value).booleanValue() ? JsonValue.TRUE
+          : JsonValue.FALSE;
+    case JsonValue:
+      return (JsonValue) value;
+    case Null:
+      return JsonValue.NULL;
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Convert JSON value to {@code String} containing value stored in JSON
+   * format.
+   * 
+   * @param value
+   *          JSON value to be converted.
+   * @return Provided value stored in JSON format.
+   */
+  public static String jsonData(final JsonValue value) {
+    final StringWriter strWriter = new StringWriter();
+    try (final JsonWriter writer = Json.createWriter(strWriter)) {
+      writer.write(value);
+    } catch (JsonException ex) {
+      System.out.println(
+          "Could not initialize JSON data: " + ex.getLocalizedMessage());
+      throw ex;
+    }
+    return strWriter.toString();
+  }
+
+  /**
+   * Apply patch on provided JSON {@code value} using {@code apply()} method of
+   * {@code JsonPatch}.
+   * 
+   * @param patch
+   *          {@code JsonPatch} with patch operations.
+   * @param value
+   *          JSON {@code value} to be patched.
+   * @return Result of JSON {@code value} patching.
+   */
+  public static JsonValue patchApply(final JsonPatch patch,
+      final JsonValue value) {
+    switch (value.getValueType()) {
+    case OBJECT:
+      return patch.apply((JsonObject) value);
+    case ARRAY:
+      return patch.apply((JsonArray) value);
+    default:
+      throw new IllegalArgumentException(
+          "Unsupported JSON value type to be pached");
+    }
+  }
+
+  /**
+   * Convert {@code boolean} value to {@code JsonValue}.
+   * 
+   * @param value
+   *          Source {@code boolean} value.
+   * @return {@code JsonValue.TRUE} if provided {@code value} is {@code true} or
+   *         {@code JsonValue.FALSE} otherwise.
+   */
+  public static JsonValue booleanValue(final boolean value) {
+    return value ? JsonValue.TRUE : JsonValue.FALSE;
+  }
+
+  /**
+   * Creates an instance of RFC 6902 operation test.
+   */
+  private SimpleValues() {
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestFail.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestFail.java
new file mode 100644
index 0000000..630c286
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestFail.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+/**
+ * Test failure notification.
+ */
+public class TestFail {
+
+  /** Test failure name and message separator. */
+  private static final String NM_SEP = ": ";
+
+  /** Name of test that failed. */
+  private final String name;
+
+  /** Error message. */
+  private final String message;
+
+  /**
+   * Creates an instance of test failure notification.
+   * 
+   * @param name
+   *          Test name.
+   * @param message
+   *          Error message.
+   */
+  public TestFail(final String name, final String message) {
+    this.name = name;
+    this.message = message;
+  }
+
+  /**
+   * Returns human readable content of test failure notification.
+   * 
+   * @return Human readable content of test failure notification.
+   */
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder(
+        name.length() + message.length() + NM_SEP.length());
+    sb.append(name);
+    sb.append(NM_SEP);
+    sb.append(message);
+    return sb.toString();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestResult.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestResult.java
new file mode 100644
index 0000000..69309f2
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/common/TestResult.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.common;
+
+import jakarta.jsonp.tck.api.common.TestFail;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.util.LinkedList;
+
+/**
+ * Tests result record.
+ */
+public class TestResult {
+
+  /** Name of test suite. */
+  private final String name;
+
+  /** List of test failures. */
+  private final LinkedList<TestFail> fails;
+
+  /**
+   * Creates an instance of tests result record.
+   * 
+   * @param name
+   *          Name of test suite.
+   */
+  public TestResult(final String name) {
+    this.name = name;
+    this.fails = new LinkedList<>();
+  }
+
+  /**
+   * Records test failure.
+   * 
+   * @param name
+   *          Test name.
+   * @param message
+   *          Error message.
+   */
+  public void fail(final String name, final String message) {
+    fails.addLast(new TestFail(name, message));
+  }
+
+  /**
+   * Evaluate test results.
+   * 
+   * @throws Fault
+   *           when any test failed.
+   */
+  public void eval() throws Fault {
+    if (fails.isEmpty()) {
+      return;
+    }
+    final StringBuilder sb = new StringBuilder();
+    sb.append(name);
+    sb.append(" failed:");
+    for (TestFail fail : fails) {
+      sb.append('\n');
+      sb.append(fail.toString());
+    }
+    throw new Fault(sb.toString());
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/exceptiontests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/exceptiontests/ClientTests.java
new file mode 100644
index 0000000..4ee38c9
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/exceptiontests/ClientTests.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.exceptiontests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+    
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+
+  /* Tests */
+
+  /*
+   * @testName: jsonExceptionConstructorTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:37;
+   * 
+   * @test_Strategy: Test API: JsonException ret = new JsonException(String)
+   */
+  @Test
+  public void jsonExceptionConstructorTest1() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect.";
+
+      System.out.println("Test JsonException(String)");
+      JsonException exception = new JsonException(message);
+      try {
+        throw exception;
+      } catch (JsonException e) {
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonExceptionConstructorTest1 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonExceptionConstructorTest1 Failed:");
+  }
+
+  /*
+   * @testName: jsonExceptionConstructorTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:38;
+   * 
+   * @test_Strategy: Test API: JsonException ret = new JsonException(String,
+   * Throwable)
+   */
+  @Test
+  public void jsonExceptionConstructorTest2() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect due to foo.";
+      Exception foo = new Exception("This is a foo exception");
+
+      System.out.println("Test JsonException(String, Throwable)");
+      JsonException exception = new JsonException(message, foo);
+
+      try {
+        throw exception;
+      } catch (JsonException e) {
+        if (!e.getCause().equals(foo)) {
+          System.err.println("Incorrect cause: expected " + foo + ", received "
+              + e.getCause());
+          pass = false;
+        }
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonExceptionConstructorTest2 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonExceptionConstructorTest2 Failed:");
+  }
+
+  /*
+   * @testName: jsonParsingExceptionConstructorTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:285; JSONP:JAVADOC:478; JSONP:JAVADOC:474;
+   * JSONP:JAVADOC:475; JSONP:JAVADOC:476;
+   * 
+   * @test_Strategy: Test API: JsonParsingException ret = new
+   * JsonParsingException(String, JsonLocation)
+   */
+  @Test
+  public void jsonParsingExceptionConstructorTest1() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect.";
+      MyJsonLocation expLoc = new MyJsonLocation(10, 20, 30);
+      System.out.println("MyJsonLocation");
+      JSONP_Util.dumpLocation(expLoc);
+
+      System.out.println("Test JsonParsingException(String, JsonLocation)");
+      JsonParsingException exception = new JsonParsingException(message,
+          expLoc);
+      try {
+        throw exception;
+      } catch (JsonParsingException e) {
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+      JsonLocation actLoc = exception.getLocation();
+      System.out.println("JsonParsingException.getLocation()");
+      JSONP_Util.dumpLocation(actLoc);
+      if (!JSONP_Util.assertEquals(expLoc, actLoc))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParsingExceptionConstructorTest1 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonParsingExceptionConstructorTest1 Failed:");
+  }
+
+  /*
+   * @testName: jsonParsingExceptionConstructorTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:286; JSONP:JAVADOC:478; JSONP:JAVADOC:474;
+   * JSONP:JAVADOC:475; JSONP:JAVADOC:476;
+   * 
+   * @test_Strategy: Test API: JsonParsingException ret = new
+   * JsonParsingException(String, Throwable, JsonLocation)
+   */
+  @Test
+  public void jsonParsingExceptionConstructorTest2() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect due to foo.";
+      Exception foo = new Exception("This is a foo exception");
+      MyJsonLocation expLoc = new MyJsonLocation(10, 20, 30);
+      System.out.println("MyJsonLocation");
+      JSONP_Util.dumpLocation(expLoc);
+
+      System.out.println("Test JsonParsingException(String, Throwable)");
+      JsonParsingException exception = new JsonParsingException(message, foo,
+          expLoc);
+
+      try {
+        throw exception;
+      } catch (JsonParsingException e) {
+        if (!e.getCause().equals(foo)) {
+          System.err.println("Incorrect cause: expected " + foo + ", received "
+              + e.getCause());
+          pass = false;
+        }
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+      JsonLocation actLoc = exception.getLocation();
+      System.out.println("JsonParsingException.getLocation()");
+      JSONP_Util.dumpLocation(actLoc);
+      if (!JSONP_Util.assertEquals(expLoc, actLoc))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParsingExceptionConstructorTest2 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonParsingExceptionConstructorTest2 Failed:");
+  }
+
+  /*
+   * @testName: jsonGenerationExceptionConstructorTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:278;
+   * 
+   * @test_Strategy: Test API: JsonGenerationException ret = new
+   * JsonGenerationException(String)
+   */
+  @Test
+  public void jsonGenerationExceptionConstructorTest1() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect.";
+
+      System.out.println("Test JsonGenerationException(String)");
+      JsonGenerationException exception = new JsonGenerationException(message);
+      try {
+        throw exception;
+      } catch (JsonGenerationException e) {
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonGenerationExceptionConstructorTest1 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonGenerationExceptionConstructorTest1 Failed:");
+  }
+
+  /*
+   * @testName: jsonGenerationExceptionConstructorTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:279;
+   * 
+   * @test_Strategy: Test API: JsonGenerationException ret = new
+   * JsonGenerationException(String, Throwable)
+   */
+  @Test
+  public void jsonGenerationExceptionConstructorTest2() throws Fault {
+    boolean pass = true;
+
+    try {
+      String message = "This JSON is incorrect due to foo.";
+      Exception foo = new Exception("This is a foo exception");
+
+      System.out.println("Test JsonGenerationException(String, Throwable)");
+      JsonGenerationException exception = new JsonGenerationException(message,
+          foo);
+
+      try {
+        throw exception;
+      } catch (JsonGenerationException e) {
+        if (!e.getCause().equals(foo)) {
+          System.err.println("Incorrect cause: expected " + foo + ", received "
+              + e.getCause());
+          pass = false;
+        }
+        if (!e.getMessage().equals(message)) {
+          System.err.println("Incorrect message: expected " + message + ", received "
+              + e.getMessage());
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonGenerationExceptionConstructorTest2 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonGenerationExceptionConstructorTest2 Failed:");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildAdd.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildAdd.java
new file mode 100644
index 0000000..3c6f463
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildAdd.java
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.jsonp.tck.api.common.ArrayBuilder;
+import jakarta.jsonp.tck.api.common.JsonIO;
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests:
+ * {@link JsonArrayBuilder} API add() methods added in JSON-P 1.1.<br>
+ */
+public class ArrayBuildAdd extends ArrayCommon {
+
+  /**
+   * Creates an instance of {@link JsonArrayBuilder} API add() methods added in
+   * JSON-P 1.1 test.
+   */
+  ArrayBuildAdd() {
+    super();
+  }
+
+  /**
+   * Test {@link JsonArrayBuilder} API add() methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonArrayBuilder API add() methods added in JSON-P 1.1.");
+    System.out.println("JsonArrayBuilder API add() methods added in JSON-P 1.1.");
+    testAdd(result);
+    testAddNullBuilder(result);
+    testAddOutOfBounds(result);
+    testAddNull(result);
+    testAddNullOutOfBounds(result);
+    testAddArrayBuilder(result);
+    testAddArrayBuilderNull(result);
+    testAddArrayBuilderOutOfBounds(result);
+    testAddObjectBuilder(result);
+    testAddObjectBuilderNull(result);
+    testAddObjectBuilderOutOfBounds(result);
+    testAddAllString(result);
+    testAddAllInt(result);
+    testAddAllBool(result);
+    testAddAllObject(result);
+    testAddAllNull(result);
+    return result;
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder add(int, Object)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAdd(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // add(int,JsonValue)
+        STR_VALUE, // add(int,String)
+        INT_VALUE, // add(int,int)
+        LNG_VALUE, // add(int,long)
+        DBL_VALUE, // add(int,double)
+        BIN_VALUE, // add(int,BigInteger)
+        BDC_VALUE, // add(int,BigDecimal)
+        BOOL_VALUE // add(int,boolean)
+    };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - add(int," + typeName + ")");
+      final String json = "[" + JsonValueType.toStringValue(value) + "]";
+      final JsonValue check = JsonIO.read(json);
+      final JsonArrayBuilder builder = createArrayBuilder(0, value);
+      final JsonValue out = builder.build();
+      if (operationFailed(check, out)) {
+        result.fail("add(" + typeName + ")", "Builder output "
+            + valueToString(out) + " value shall be " + valueToString(check));
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int, Object)} method on {@code String}
+   * array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddOutOfBounds(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // add(int,JsonValue)
+        STR_VALUE, // add(int,String)
+        INT_VALUE, // add(int,int)
+        LNG_VALUE, // add(int,long)
+        DBL_VALUE, // add(int,double)
+        BIN_VALUE, // add(int,BigInteger)
+        BDC_VALUE, // add(int,BigDecimal)
+        BOOL_VALUE // add(int,boolean)
+    };
+    final int[] indexes = new int[] { -1, 2, 3 };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - add(int," + typeName + ")");
+      final String json = "[" + JsonValueType.toStringValue(value) + "]";
+      // Add value into the array for the first time to het array of size 1.
+      JsonArrayBuilder builder = createArrayBuilder(value);
+      for (int index : indexes) {
+        try {
+          // Add value on out of bounds index
+          builder = updateOperationBuilder(builder, index, value);
+          result.fail("add(int," + typeName + ")",
+              "Calling method with out of bounds index=" + index
+                  + " argument shall throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+          System.out.println("    - Expected exception for index=" + index + ": "
+              + e.getMessage());
+        } catch (Throwable t) {
+          result.fail("add(int,(" + typeName + ")null)",
+              "Calling method with with out of bounds index=" + index
+                  + " argument shall throw IndexOutOfBoundsException, not "
+                  + t.getClass().getSimpleName());
+        }
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int, Object)} method on {@code String}
+   * array with null value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddNullBuilder(final TestResult result) {
+    final JsonValueType[] types = new JsonValueType[] { JsonValueType.JsonValue, // add(int,(JsonValue)null)
+        JsonValueType.String, // add(int,(String)null)
+        JsonValueType.BigInteger, // add(int,(BigInteger)null)
+        JsonValueType.BigDecimal // add(int,(BigDecimal)null)
+    };
+    for (JsonValueType type : types) {
+      final String typeName = type.name();
+      System.out.println(" - add(int,(" + typeName + ")null)");
+      try {
+        ArrayBuilder.add(Json.createArrayBuilder(), 0, type);
+        result.fail("add(int,(" + typeName + ")null)",
+            "Calling method with null argument shall throw NullPointerException");
+      } catch (NullPointerException e) {
+        System.out.println("    - Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("add(int,(" + typeName + ")null)",
+            "Calling method with null argument shall throw NullPointerException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addNull(int)} method on {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddNull(final TestResult result) {
+    System.out.println(" - addNull(int)");
+    final Object value = null;
+    final String json = "[" + JsonValueType.toStringValue(value) + "]";
+    final JsonValue check = JsonIO.read(json);
+    final JsonArrayBuilder builder = createArrayBuilder(0, value);
+    final JsonValue out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("addNull(int)", "Builder output " + valueToString(out)
+          + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addNull(int)} method on {@code String} array
+   * with index being out of range ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddNullOutOfBounds(final TestResult result) {
+    final int[] indexes = new int[] { -1, 2, 3 };
+    System.out.println(" - addNull(int)");
+    final Object value = null;
+    JsonArrayBuilder builder = createArrayBuilder(value);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        builder = updateOperationBuilder(builder, index, value);
+        result.fail("addNull(int)", "Calling method with out of bounds index="
+            + index + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("addNull(int)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,JsonArrayBuilder)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddArrayBuilder(final TestResult result) {
+    System.out.println(" - add(int,JsonArrayBuilder)");
+    final JsonValue checkBeg = JsonIO
+        .read("[[" + JsonValueType.toStringValue(STR_VALUE_1) + ","
+            + JsonValueType.toStringValue(STR_VALUE_2) + ","
+            + JsonValueType.toStringValue(STR_VALUE_3) + ","
+            + JsonValueType.toStringValue(STR_VALUE_4) + "],"
+            + JsonValueType.toStringValue(STR_VALUE_5) + "]");
+    final JsonValue checkEnd = JsonIO
+        .read("[" + JsonValueType.toStringValue(STR_VALUE_1) + ",["
+            + JsonValueType.toStringValue(STR_VALUE_2) + ","
+            + JsonValueType.toStringValue(STR_VALUE_3) + ","
+            + JsonValueType.toStringValue(STR_VALUE_4) + ","
+            + JsonValueType.toStringValue(STR_VALUE_5) + "]]");
+    final JsonArrayBuilder inBeg = createArrayBuilder(STR_VALUE_5);
+    final JsonArrayBuilder inEnd = createArrayBuilder(STR_VALUE_1);
+    final JsonArrayBuilder argBeg = createArrayBuilder(STR_VALUE_1)
+        .add(STR_VALUE_2).add(STR_VALUE_3).add(STR_VALUE_4);
+    final JsonArrayBuilder argEnd = createArrayBuilder(STR_VALUE_2)
+        .add(STR_VALUE_3).add(STR_VALUE_4).add(STR_VALUE_5);
+    verifyAddBuilder(result, checkBeg, 0, argBeg, inBeg);
+    verifyAddBuilder(result, checkEnd, 1, argEnd, inEnd);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,(JsonArrayBuilder)null)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddArrayBuilderNull(final TestResult result) {
+    System.out.println(" - add(int,(JsonArrayBuilder)null)");
+    final JsonArrayBuilder in = createArrayBuilder(DEF_VALUE);
+    final JsonArrayBuilder arg = null;
+    try {
+      in.add(0, arg);
+      result.fail("add(int,(JsonArrayBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("add(int,(JsonArrayBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,JsonArrayBuilder)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddArrayBuilderOutOfBounds(final TestResult result) {
+    System.out.println(" - add(int,JsonArrayBuilder)");
+    final int[] indexes = new int[] { -1, 5, 6 };
+    final JsonArrayBuilder in = createArrayBuilder(STR_VALUE_1).add(STR_VALUE_2)
+        .add(STR_VALUE_3).add(STR_VALUE_4);
+    final JsonArrayBuilder arg = createArrayBuilder(STR_VALUE_5);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        in.add(index, arg);
+        result.fail("add(int,JsonArrayBuilder)",
+            "Calling method with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("add(int,JsonArrayBuilder)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,JsonObjectBuilder)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddObjectBuilder(final TestResult result) {
+    System.out.println(" - add(int,JsonObjectBuilder)");
+    final JsonValue checkBeg = JsonIO
+        .read("[{" + JsonValueType.toStringValue(STR_NAME) + ":"
+            + JsonValueType.toStringValue(STR_VALUE) + "},"
+            + JsonValueType.toStringValue(STR_VALUE_1) + "]");
+    final JsonValue checkEnd = JsonIO
+        .read("[" + JsonValueType.toStringValue(STR_VALUE_1) + ",{"
+            + JsonValueType.toStringValue(STR_NAME) + ":"
+            + JsonValueType.toStringValue(STR_VALUE) + "}]");
+    final JsonArrayBuilder inBeg = createArrayBuilder(STR_VALUE_1);
+    final JsonArrayBuilder inEnd = createArrayBuilder(STR_VALUE_1);
+    final JsonObjectBuilder argBeg = Json.createObjectBuilder().add(STR_NAME,
+        STR_VALUE);
+    final JsonObjectBuilder argEnd = Json.createObjectBuilder().add(STR_NAME,
+        STR_VALUE);
+    verifyAddBuilder(result, checkBeg, 0, argBeg, inBeg);
+    verifyAddBuilder(result, checkEnd, 1, argEnd, inEnd);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,(JsonObjectBuilder)null)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddObjectBuilderNull(final TestResult result) {
+    System.out.println(" - add(int,(JsonObjectBuilder)null)");
+    final JsonArrayBuilder in = createArrayBuilder(DEF_VALUE);
+    final JsonObjectBuilder arg = null;
+    try {
+      in.add(0, arg);
+      result.fail("add(int,(JsonObjectBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("add(int,(JsonObjectBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder add(int,JsonObjectBuilder)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddObjectBuilderOutOfBounds(final TestResult result) {
+    System.out.println(" - add(int,JsonObjectBuilder)");
+    final int[] indexes = new int[] { -1, 5, 6 };
+    final JsonArrayBuilder in = createArrayBuilder(STR_VALUE_1).add(STR_VALUE_2)
+        .add(STR_VALUE_3).add(STR_VALUE_4);
+    final JsonObjectBuilder arg = Json.createObjectBuilder().add(STR_NAME,
+        STR_VALUE);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        in.add(index, arg);
+        result.fail("add(int,JsonObjectBuilder)",
+            "Calling method with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("add(int,JsonObjectBuilder)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddAllString(final TestResult result) {
+    System.out.println(" - addAll(JsonArrayBuilder) for String array");
+    final JsonArray check = createSimpleStringArray5();
+    final String[] strings = new String[] { STR_VALUE_1, STR_VALUE_2,
+        STR_VALUE_3, STR_VALUE_4, STR_VALUE_5 };
+    verifyAddAll(result, check, strings);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * {@code int) array. @param result Test suite result.
+   */
+  private void testAddAllInt(final TestResult result) {
+    System.out.println(" - addAll(JsonArrayBuilder) for int array");
+    final JsonArray check = createSimpleIntArray5();
+    final Integer[] ints = new Integer[] { INT_VALUE_1, INT_VALUE_2,
+        INT_VALUE_3, INT_VALUE_4, INT_VALUE_5 };
+    verifyAddAll(result, check, ints);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * {@code boolean) array. @param result Test suite result.
+   */
+  private void testAddAllBool(final TestResult result) {
+    System.out.println(" - addAll(JsonArrayBuilder) for boolean array");
+    final JsonArray check = createSimpleBoolArray5();
+    final Boolean[] bools = new Boolean[] { BOOL_FALSE, BOOL_TRUE, BOOL_TRUE,
+        BOOL_FALSE, BOOL_TRUE };
+    verifyAddAll(result, check, bools);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * {@code JsonObject) array. @param result Test suite result.
+   */
+  private void testAddAllObject(final TestResult result) {
+    System.out.println(" - addAll(JsonArrayBuilder) for JsonObject array");
+    final JsonArray check = createSimpleObjectArray5();
+    final JsonObject[] bools = new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_2,
+        OBJ_VALUE_3, OBJ_VALUE_4, OBJ_VALUE_5 };
+    verifyAddAll(result, check, bools);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * {@code null} builder argument.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddAllNull(final TestResult result) {
+    System.out.println(" - addAll(JsonArrayBuilder) for null builder argument");
+    JsonArrayBuilder builder = Json.createArrayBuilder();
+    try {
+      builder.addAll((JsonArrayBuilder) null);
+      result.fail("addAll(null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("addAll(null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code default JsonArrayBuilder addAll(JsonArrayBuilder)} method on
+   * provided array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param values
+   *          Values used to build JSON array builder.
+   */
+  private void verifyAddAll(final TestResult result, final JsonArray check,
+      final Object[] values) {
+    JsonArrayBuilder builderIn = Json.createArrayBuilder();
+    for (Object value : values) {
+      builderIn = updateOperationBuilder(builderIn, value);
+    }
+    final JsonArrayBuilder builderOut = Json.createArrayBuilder();
+    builderOut.addAll(builderIn);
+    final JsonArray out = builderOut.build();
+    if (operationFailed(check, out)) {
+      result.fail("addAll(JsonArrayBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code default JsonArrayBuilder add(int,JsonArrayBuilder)} method on
+   * provided builders.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param src
+   *          Source builder (the one to be added).
+   * @param target
+   *          Target builder (to which to add).
+   */
+  private void verifyAddBuilder(final TestResult result, final JsonValue check,
+      final int index, final JsonArrayBuilder src,
+      final JsonArrayBuilder target) {
+    final JsonArray out = target.add(index, src).build();
+    if (operationFailed(check, out)) {
+      result.fail("add(int,JsonArrayBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code default JsonArrayBuilder add(int,JsonObjectBuilder)} method on
+   * provided builders.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param src
+   *          Source builder (the one to be added).
+   * @param target
+   *          Target builder (to which to add).
+   */
+  private void verifyAddBuilder(final TestResult result, final JsonValue check,
+      final int index, final JsonObjectBuilder src,
+      final JsonArrayBuilder target) {
+    final JsonArray out = target.add(index, src).build();
+    if (operationFailed(check, out)) {
+      result.fail("add(int,JsonObjectBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Create and initialize array builder to contain single value. Child class
+   * callback.
+   * 
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(final Object value) {
+    return ArrayBuilder.add(Json.createArrayBuilder(), value);
+  }
+
+  /**
+   * Create and initialize array builder to contain single value. Child class
+   * callback.
+   * 
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(final int index,
+      final Object value) {
+    return ArrayBuilder.add(Json.createArrayBuilder(), index, value);
+  }
+
+  /**
+   * Update array builder to contain next value. Child class callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final Object value) {
+    return ArrayBuilder.add(builder, value);
+  }
+
+  /**
+   * Update array builder to contain next value. Child class callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final int index, final Object value) {
+    return ArrayBuilder.add(builder, index, value);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildRemove.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildRemove.java
new file mode 100644
index 0000000..2f5b061
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildRemove.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.jsonp.tck.api.common.ArrayBuilder;
+import jakarta.jsonp.tck.api.common.JsonIO;
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests:
+ * {@link JsonArrayBuilder} API remove() methods added in JSON-P 1.1.<br>
+ */
+public class ArrayBuildRemove extends ArrayCommon {
+
+  /**
+   * Creates an instance of {@link JsonArrayBuilder} API remove() methods added
+   * in JSON-P 1.1 test.
+   */
+  ArrayBuildRemove() {
+    super();
+  }
+
+  /**
+   * {@link JsonArrayBuilder} API remove() methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonArrayBuilder API remove() methods added in JSON-P 1.1.");
+    System.out.println("JsonArrayBuilder API remove() methods added in JSON-P 1.1.");
+    testRemove(result);
+    testRemoveOutOfBounds(result);
+    return result;
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder remove(int, Object)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemove(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // remove(int,JsonValue)
+        STR_VALUE, // remove(int,String)
+        INT_VALUE, // remove(int,int)
+        LNG_VALUE, // remove(int,long)
+        DBL_VALUE, // remove(int,double)
+        BIN_VALUE, // remove(int,BigInteger)
+        BDC_VALUE, // remove(int,BigDecimal)
+        BOOL_VALUE // remove(int,boolean)
+    };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - remove(int," + typeName + ")");
+      final String json = "[]";
+      final JsonValue check = JsonIO.read(json);
+      JsonArrayBuilder builder = ArrayBuilder.add(Json.createArrayBuilder(),
+          value);
+      builder = updateOperationBuilder(builder, 0, null);
+      final JsonValue out = builder.build();
+      if (operationFailed(check, out)) {
+        result.fail("remove(" + typeName + ")", "Builder output "
+            + valueToString(out) + " value shall be " + valueToString(check));
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder remove(int, Object)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveOutOfBounds(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // remove(int,JsonValue)
+        STR_VALUE, // remove(int,String)
+        INT_VALUE, // remove(int,int)
+        LNG_VALUE, // remove(int,long)
+        DBL_VALUE, // remove(int,double)
+        BIN_VALUE, // remove(int,BigInteger)
+        BDC_VALUE, // remove(int,BigDecimal)
+        BOOL_VALUE // remove(int,boolean)
+    };
+    final int[] indexes = new int[] { -1, 2, 3 };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - remove(int," + typeName + ")");
+      final String json = "[" + JsonValueType.toStringValue(value) + "]";
+      // Add value into the array for the first time to het array of size 1.
+      JsonArrayBuilder builder = ArrayBuilder.add(Json.createArrayBuilder(),
+          value);
+      for (int index : indexes) {
+        try {
+          // Add value on out of bounds index
+          builder = updateOperationBuilder(builder, index, null);
+          result.fail("remove(int," + typeName + ")",
+              "Calling method with out of bounds index=" + index
+                  + " argument shall throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+          System.out.println("    - Expected exception for index=" + index + ": "
+              + e.getMessage());
+        } catch (Throwable t) {
+          result.fail("remove(int,(" + typeName + ")null)",
+              "Calling method with with out of bounds index=" + index
+                  + " argument shall throw IndexOutOfBoundsException, not "
+                  + t.getClass().getSimpleName());
+        }
+      }
+    }
+  }
+
+  /**
+   * Create and initialize array builder. Unsupported method call for remove()
+   * method.
+   * 
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(Object value) {
+    throw new UnsupportedOperationException(
+        "Method remove is not implemented.");
+  }
+
+  /**
+   * Create and initialize array builder. Unsupported method call for remove()
+   * method.
+   * 
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(final int index,
+      final Object value) {
+    throw new UnsupportedOperationException(
+        "Method remove is not implemented.");
+  }
+
+  /**
+   * Update array builder. Unsupported method call for remove() method.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(JsonArrayBuilder builder,
+      Object value) {
+    throw new UnsupportedOperationException(
+        "Method remove is not implemented.");
+  }
+
+  /**
+   * Update array builder with value removal at specified index. Child class
+   * callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value argument is ignored.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final int index, final Object value) {
+    return ArrayBuilder.remove(builder, index);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildSet.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildSet.java
new file mode 100644
index 0000000..e41ebe1
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuildSet.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.jsonp.tck.api.common.ArrayBuilder;
+import jakarta.jsonp.tck.api.common.JsonIO;
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests:
+ * {@link JsonArrayBuilder} API set() methods added in JSON-P 1.1.<br>
+ */
+public class ArrayBuildSet extends ArrayCommon {
+
+  /**
+   * Creates an instance of {@link JsonArrayBuilder} API set() methods added in
+   * JSON-P 1.1 test.
+   */
+  ArrayBuildSet() {
+    super();
+  }
+
+  /**
+   * {@link JsonArrayBuilder} API set() methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonArrayBuilder API set() methods added in JSON-P 1.1.");
+    System.out.println("JsonArrayBuilder API set() methods added in JSON-P 1.1.");
+    testSet(result);
+    testSetOutOfBounds(result);
+    testSetNullBuilder(result);
+    testSetNull(result);
+    testSetNullOutOfBounds(result);
+    testSetArrayBuilder(result);
+    testSetArrayBuilderNull(result);
+    testSetArrayBuilderOutOfBounds(result);
+    testSetObjectBuilder(result);
+    testSetObjectBuilderNull(result);
+    testSetObjectBuilderOutOfBounds(result);
+    return result;
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int, Object)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSet(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // set(int,JsonValue)
+        STR_VALUE, // set(int,String)
+        INT_VALUE, // set(int,int)
+        LNG_VALUE, // set(int,long)
+        DBL_VALUE, // set(int,double)
+        BIN_VALUE, // set(int,BigInteger)
+        BDC_VALUE, // set(int,BigDecimal)
+        BOOL_VALUE // set(int,boolean)
+    };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - set(int," + typeName + ")");
+      final String json = "[" + JsonValueType.toStringValue(value) + "]";
+      final JsonValue check = JsonIO.read(json);
+      final JsonArrayBuilder builder = updateOperationBuilder(
+          Json.createArrayBuilder().add(DEF_OBJ_VALUE), 0, value);
+      final JsonValue out = builder.build();
+      if (operationFailed(check, out)) {
+        result.fail("set(" + typeName + ")", "Builder output "
+            + valueToString(out) + " value shall be " + valueToString(check));
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int, Object)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetOutOfBounds(final TestResult result) {
+    final Object[] values = new Object[] { OBJ_VALUE, // set(int,JsonValue)
+        STR_VALUE, // set(int,String)
+        INT_VALUE, // set(int,int)
+        LNG_VALUE, // set(int,long)
+        DBL_VALUE, // set(int,double)
+        BIN_VALUE, // set(int,BigInteger)
+        BDC_VALUE, // set(int,BigDecimal)
+        BOOL_VALUE // set(int,boolean)
+    };
+    final int[] indexes = new int[] { -1, 2, 3 };
+    for (Object value : values) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - set(int," + typeName + ")");
+      final String json = "[" + JsonValueType.toStringValue(value) + "]";
+      JsonArrayBuilder builder = ArrayBuilder.add(Json.createArrayBuilder(),
+          DEF_OBJ_VALUE);
+      for (int index : indexes) {
+        try {
+          builder = updateOperationBuilder(
+              Json.createArrayBuilder().add(DEF_OBJ_VALUE), index, value);
+        } catch (IndexOutOfBoundsException e) {
+          System.out.println("    - Expected exception for index=" + index + ": "
+              + e.getMessage());
+        } catch (Throwable t) {
+          result.fail("set(int," + typeName + ")",
+              "Calling method with with out of bounds index=" + index
+                  + " argument shall throw IndexOutOfBoundsException, not "
+                  + t.getClass().getSimpleName());
+        }
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int, Object)} method on
+   * {@code String} array with null value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetNullBuilder(final TestResult result) {
+    final JsonValueType[] types = new JsonValueType[] { JsonValueType.JsonValue, // set(int,(JsonValue)null)
+        JsonValueType.String, // set(int,(String)null)
+        JsonValueType.BigInteger, // set(int,(BigInteger)null)
+        JsonValueType.BigDecimal // set(int,(BigDecimal)null)
+    };
+    for (JsonValueType type : types) {
+      final String typeName = type.name();
+      System.out.println(" - set(int,(" + typeName + ")null)");
+      try {
+        ArrayBuilder.set(Json.createArrayBuilder(), 0, type);
+        result.fail("set(int,(" + typeName + ")null)",
+            "Calling method with null argument shall throw NullPointerException");
+      } catch (NullPointerException e) {
+        System.out.println("    - Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("set(int,(" + typeName + ")null)",
+            "Calling method with null argument shall throw NullPointerException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder setNull(int)} method on {@code String}
+   * array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetNull(final TestResult result) {
+    System.out.println(" - setNull(int)");
+    final Object value = null;
+    final String json = "[" + JsonValueType.toStringValue(null) + "]";
+    final JsonValue check = JsonIO.read(json);
+    final JsonArrayBuilder builder = ArrayBuilder
+        .set(Json.createArrayBuilder().add(DEF_OBJ_VALUE), 0, value);
+    final JsonValue out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("setNull(int)", "Builder output " + valueToString(out)
+          + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder setNull(int)} method on {@code String}
+   * array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetNullOutOfBounds(final TestResult result) {
+    final int[] indexes = new int[] { -1, 2, 3 };
+    System.out.println(" - setNull(int)");
+    final Object value = null;
+    JsonArrayBuilder builder = ArrayBuilder.add(Json.createArrayBuilder(),
+        value);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        builder = updateOperationBuilder(builder, index, value);
+        result.fail("setNull(int)", "Calling method with out of bounds index="
+            + index + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("setNull(int)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,JsonArrayBuilder)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetArrayBuilder(final TestResult result) {
+    System.out.println(" - set(int,JsonArrayBuilder)");
+    final JsonValue check = JsonIO
+        .read("[[" + JsonValueType.toStringValue(STR_VALUE_1) + ","
+            + JsonValueType.toStringValue(STR_VALUE_2) + ","
+            + JsonValueType.toStringValue(STR_VALUE_3) + ","
+            + JsonValueType.toStringValue(STR_VALUE_4) + "]]");
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(STR_VALUE_5);
+    final JsonArrayBuilder arg = Json.createArrayBuilder().add(STR_VALUE_1)
+        .add(STR_VALUE_2).add(STR_VALUE_3).add(STR_VALUE_4);
+    verifySetBuilder(result, check, 0, arg, in);
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,(JsonArrayBuilder)null)}
+   * method on {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetArrayBuilderNull(final TestResult result) {
+    System.out.println(" - set(int,(JsonArrayBuilder)null)");
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(DEF_VALUE);
+    final JsonArrayBuilder arg = null;
+    try {
+      in.set(0, arg);
+      result.fail("set(int,(JsonArrayBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("set(int,(JsonArrayBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,JsonArrayBuilder)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetArrayBuilderOutOfBounds(final TestResult result) {
+    System.out.println(" - set(int,JsonArrayBuilder)");
+    final int[] indexes = new int[] { -1, 5, 6 };
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(STR_VALUE_1)
+        .add(STR_VALUE_2).add(STR_VALUE_3).add(STR_VALUE_4);
+    final JsonArrayBuilder arg = Json.createArrayBuilder().add(STR_VALUE_5);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        in.set(index, arg);
+        result.fail("set(int,JsonArrayBuilder)",
+            "Calling method with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("set(int,JsonArrayBuilder)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,JsonObjectBuilder)} method on
+   * {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetObjectBuilder(final TestResult result) {
+    System.out.println(" - set(int,JsonObjectBuilder)");
+    final JsonValue check = JsonIO
+        .read("[{" + JsonValueType.toStringValue(STR_NAME) + ":"
+            + JsonValueType.toStringValue(STR_VALUE) + "}]");
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(STR_VALUE_1);
+    final JsonObjectBuilder arg = Json.createObjectBuilder().add(STR_NAME,
+        STR_VALUE);
+    verifySetBuilder(result, check, 0, arg, in);
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,(JsonObjectBuilder)null)}
+   * method on {@code String} array.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetObjectBuilderNull(final TestResult result) {
+    System.out.println(" - set(int,(JsonObjectBuilder)null)");
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(DEF_VALUE);
+    final JsonObjectBuilder arg = null;
+    try {
+      in.set(0, arg);
+      result.fail("set(int,(JsonObjectBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("set(int,(JsonObjectBuilder)null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code default JsonArrayBuilder set(int,JsonObjectBuilder)} method on
+   * {@code String} array with index being out of range
+   * ({@code index < 0 || index > array size}).
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testSetObjectBuilderOutOfBounds(final TestResult result) {
+    System.out.println(" - set(int,JsonObjectBuilder)");
+    final int[] indexes = new int[] { -1, 5, 6 };
+    final JsonArrayBuilder in = Json.createArrayBuilder().add(STR_VALUE_1)
+        .add(STR_VALUE_2).add(STR_VALUE_3).add(STR_VALUE_4);
+    final JsonObjectBuilder arg = Json.createObjectBuilder().add(STR_NAME,
+        STR_VALUE);
+    for (int index : indexes) {
+      try {
+        // Add value on out of bounds index
+        in.set(index, arg);
+        result.fail("set(int,JsonObjectBuilder)",
+            "Calling method with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException");
+      } catch (IndexOutOfBoundsException e) {
+        System.out.println("    - Expected exception for index=" + index + ": "
+            + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("set(int,JsonObjectBuilder)",
+            "Calling method with with out of bounds index=" + index
+                + " argument shall throw IndexOutOfBoundsException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code default JsonArrayBuilder set(int,JsonArrayBuilder)} method on
+   * provided builders.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param src
+   *          Source builder (the one to be added).
+   * @param target
+   *          Target builder (to which to add).
+   */
+  private void verifySetBuilder(final TestResult result, final JsonValue check,
+      final int index, final JsonArrayBuilder src,
+      final JsonArrayBuilder target) {
+    final JsonArray out = target.set(index, src).build();
+    if (operationFailed(check, out)) {
+      result.fail("set(int,JsonArrayBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code default JsonArrayBuilder set(int,JsonObjectBuilder)} method on
+   * provided builders.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param src
+   *          Source builder (the one to be added).
+   * @param target
+   *          Target builder (to which to add).
+   */
+  private void verifySetBuilder(final TestResult result, final JsonValue check,
+      final int index, final JsonObjectBuilder src,
+      final JsonArrayBuilder target) {
+    final JsonArray out = target.set(index, src).build();
+    if (operationFailed(check, out)) {
+      result.fail("set(int,JsonObjectBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Create and initialize array builder to contain single value. Unsupported
+   * method call for set() method.
+   * 
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(Object value) {
+    throw new UnsupportedOperationException("Method set is not implemented.");
+  }
+
+  /**
+   * Create and initialize array builder to contain single value. Child class
+   * callback.
+   * 
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  @Override
+  protected JsonArrayBuilder createArrayBuilder(final int index,
+      final Object value) {
+    return ArrayBuilder.set(Json.createArrayBuilder(), index, value);
+  }
+
+  /**
+   * Update array builder to contain next value. Unsupported method call for
+   * set() method.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(JsonArrayBuilder builder,
+      Object value) {
+    throw new UnsupportedOperationException("Method set is not implemented.");
+  }
+
+  /**
+   * Update array builder to contain next value. Child class callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  @Override
+  protected JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final int index, final Object value) {
+    return ArrayBuilder.set(builder, index, value);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuilders.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuilders.java
new file mode 100644
index 0000000..9a71d27
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayBuilders.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.util.ArrayList;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+import java.util.Iterator;
+import java.util.List;
+import jakarta.json.JsonNumber;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests:
+ * {@link JsonArrayBuilder} API factory methods added in JSON-P 1.1.<br>
+ */
+public class ArrayBuilders {
+
+  /**
+   * Creates an instance of {@link JsonArrayBuilder} API factory methods added
+   * in JSON-P 1.1 test.
+   */
+  ArrayBuilders() {
+    super();
+  }
+
+  /**
+   * Test {@link JsonArrayBuilder} factory method added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonArrayBuilder API factory methods added in JSON-P 1.1.");
+    System.out.println("JsonArrayBuilder API factory methods added in JSON-P 1.1.");
+    testCreateFromCollection(result);
+    testCreateFromJsonArray(result);
+    testGetStringValuesAs(result);
+    testGetIntValuesAs(result);
+    return result;
+  }
+
+  /**
+   * Test {@link Json#createArrayBuilder(Collection<Object>)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateFromCollection(final TestResult result) {
+    System.out.println(" - Json#createArrayBuilder(Collection<Object>)");
+    final JsonArray check = createSimpleStringArray5();
+    final ArrayList<Object> values = new ArrayList<>(check.size());
+    for (final JsonValue value : check) {
+      values.add(((JsonString) value).getString());
+    }
+    final JsonArrayBuilder builder = Json.createArrayBuilder(values);
+    final JsonArray out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("createArrayBuilder(Collection<Object>)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@link Json#createArrayBuilder(JsonArray)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateFromJsonArray(final TestResult result) {
+    System.out.println(" - Json#createArrayBuilder(JsonArray)");
+    final JsonArray check = createSimpleStringArray5();
+    final JsonArrayBuilder builder = Json.createArrayBuilder(check);
+    final JsonArray out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("createArrayBuilder(JsonArray)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@code default JsonArray#getValuesAs(Function<K,T>)} method on
+   * {@code String} values.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testGetStringValuesAs(final TestResult result) {
+    System.out.println(" - getValuesAs(Function<K,T> on String array");
+    final JsonArray in = createStringArray2();
+    final List<String> out = in.getValuesAs(JsonString::getString);
+    boolean failed = in.size() != out.size();
+    if (!failed) {
+      final Iterator<JsonValue> inIt = in.iterator();
+      final Iterator<String> outIt = out.iterator();
+      while (!failed && inIt.hasNext()) {
+        final JsonValue inVal = inIt.next();
+        final String outVal = outIt.next();
+        failed = !((JsonString) inVal).getString().equals(outVal);
+      }
+    }
+    if (failed) {
+      result.fail("getValuesAs(Function<K,T>)", "Returned Array "
+          + out.toString() + " content shall match " + valueToString(in));
+    }
+  }
+
+  /**
+   * Test {@code default JsonArray#getValuesAs(Function<K,T>)} method on
+   * {@code int} values.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testGetIntValuesAs(final TestResult result) {
+    System.out.println(" - getValuesAs(Function<K,T> on int array");
+    final JsonArray in = createIntArray2();
+    final List<Integer> out = in.getValuesAs(JsonNumber::intValue);
+    boolean failed = in.size() != out.size();
+    if (!failed) {
+      final Iterator<JsonValue> inIt = in.iterator();
+      final Iterator<Integer> outIt = out.iterator();
+      while (!failed && inIt.hasNext()) {
+        final JsonValue inVal = inIt.next();
+        final Integer outVal = outIt.next();
+        failed = ((JsonNumber) inVal).intValue() != outVal;
+      }
+    }
+    if (failed) {
+      result.fail("getValuesAs(Function<K,T>)", "Returned Array "
+          + out.toString() + " content shall match " + valueToString(in));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayCommon.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayCommon.java
new file mode 100644
index 0000000..1560b36
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ArrayCommon.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for {@link JsonArray}
+ * and {@link JsonArrayBuilder}.
+ */
+public abstract class ArrayCommon {
+
+  /**
+   * Create and initialize array builder to contain single value. Child class
+   * callback.
+   * 
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  protected abstract JsonArrayBuilder createArrayBuilder(final Object value);
+
+  /**
+   * Create and initialize array builder to contain single value. Child class
+   * callback.
+   * 
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder containing value.
+   */
+  protected abstract JsonArrayBuilder createArrayBuilder(final int index,
+      final Object value);
+
+  /**
+   * Update array builder to contain next value. Child class callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  protected abstract JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final Object value);
+
+  /**
+   * Update array builder to contain next value. Child class callback.
+   * 
+   * @param builder
+   *          JSON array builder to update.
+   * @param index
+   *          Position in the array where value is added.
+   * @param value
+   *          JSON value stored in the builder. Value of {@code null} is stored
+   *          as JSON {@code null} keyword.
+   * @return JSON array builder with value updated.
+   */
+  protected abstract JsonArrayBuilder updateOperationBuilder(
+      final JsonArrayBuilder builder, final int index, final Object value);
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ClientTests.java
new file mode 100644
index 0000000..0fa1978
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonarraytests/ClientTests.java
@@ -0,0 +1,1428 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonarraytests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+
+//$Id$
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+    
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonArrayTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:6; JSONP:JAVADOC:8; JSONP:JAVADOC:10;
+   * JSONP:JAVADOC:12; JSONP:JAVADOC:14; JSONP:JAVADOC:16; JSONP:JAVADOC:18;
+   * JSONP:JAVADOC:25; JSONP:JAVADOC:21; JSONP:JAVADOC:400; JSONP:JAVADOC:401;
+   * JSONP:JAVADOC:402; JSONP:JAVADOC:403; JSONP:JAVADOC:404; JSONP:JAVADOC:406;
+   * JSONP:JAVADOC:408; JSONP:JAVADOC:409;
+   * 
+   * @test_Strategy: Tests JsonArray/JsonArrayBuilder API's. Build a JsonArray
+   * using the JsonArrayBuilder API's then verify that the list of JsonArray
+   * values matches the expected list of JsonArray values.
+   */
+  @Test
+  public void jsonArrayTest1() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create the expected list of JsonArray values");
+      List<JsonValue> expList = new ArrayList<>();
+      expList.add(JsonValue.FALSE);
+      expList.add(JsonValue.TRUE);
+      expList.add(JsonValue.NULL);
+      expList.add(JSONP_Util.createJsonNumber(Double.MIN_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Double.MAX_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Integer.MIN_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Integer.MAX_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Long.MIN_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Long.MAX_VALUE));
+      expList.add(
+          JSONP_Util.createJsonNumber(BigDecimal.valueOf(123456789.123456789)));
+      expList.add(JSONP_Util.createJsonNumber(new BigInteger("123456789")));
+      expList.add(JSONP_Util.createJsonString("string1"));
+      expList.add(object);
+      expList.add(array);
+      JSONP_Util.dumpList(expList, "Expected List");
+
+      System.out.println("Create JsonArray using all JsonArrayBuilder API's");
+      JsonArray myJsonArray = Json.createArrayBuilder() // Indices
+          .add(JsonValue.FALSE) // 0
+          .add(JsonValue.TRUE) // 1
+          .add(JsonValue.NULL) // 2
+          .add(Double.MIN_VALUE) // 3
+          .add(Double.MAX_VALUE) // 4
+          .add(Integer.MIN_VALUE) // 5
+          .add(Integer.MAX_VALUE) // 6
+          .add(Long.MIN_VALUE) // 7
+          .add(Long.MAX_VALUE) // 8
+          .add(BigDecimal.valueOf(123456789.123456789)) // 9
+          .add(new BigInteger("123456789")) // 10
+          .add("string1") // 11
+          .add(object) // 12
+          .add(array) // 13
+          .build();
+
+      List<JsonValue> actualList = myJsonArray;
+      JSONP_Util.dumpList(actualList, "Actual List");
+      System.out.println(
+          "Compare actual list of JsonArray values with expected list of JsonArray values");
+      pass = JSONP_Util.assertEqualsList(expList, actualList);
+    } catch (Exception e) {
+      throw new Fault("jsonArrayTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonArrayTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonArrayTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:6; JSONP:JAVADOC:8; JSONP:JAVADOC:10;
+   * JSONP:JAVADOC:12; JSONP:JAVADOC:14; JSONP:JAVADOC:16; JSONP:JAVADOC:18;
+   * JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:108; JSONP:JAVADOC:96;
+   * JSONP:JAVADOC:97; JSONP:JAVADOC:21; JSONP:JAVADOC:25; JSONP:JAVADOC:184;
+   * JSONP:JAVADOC:400; JSONP:JAVADOC:401; JSONP:JAVADOC:402; JSONP:JAVADOC:403;
+   * JSONP:JAVADOC:404; JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409;
+   * 
+   * 
+   * @test_Strategy: Tests JsonArray/JsonArrayBuilder API's. Build a JsonArray
+   * using the JsonArrayBuilder API's. Write the JsonArray to a JsonWriter and
+   * read it back using a JsonReader. Verify that JsonArray written to the
+   * JsonWriter and then read back using the JsonReader are equal.
+   */
+  @Test
+  public void jsonArrayTest2() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      System.out.println(
+          "Create JsonArray 'myJsonArray1' using all JsonArrayBuilder API's");
+      JsonArray myJsonArray1 = Json.createArrayBuilder() // Indices
+          .add(JsonValue.FALSE) // 0
+          .add(JsonValue.TRUE) // 1
+          .add(JsonValue.NULL) // 2
+          .add(Double.MIN_VALUE) // 3
+          .add(Double.MAX_VALUE) // 4
+          .add(Integer.MIN_VALUE) // 5
+          .add(Integer.MAX_VALUE) // 6
+          .add(Long.MIN_VALUE) // 7
+          .add(Long.MAX_VALUE) // 8
+          .add(BigDecimal.valueOf(123456789.123456789)) // 9
+          .add(new BigInteger("123456789")) // 10
+          .add("string1") // 11
+          .add(object) // 12
+          .add(array) // 13
+          .build();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      StringWriter sw = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sw)) {
+        writer.writeArray(myJsonArray1);
+        System.out.println("Close JsonWriter");
+      }
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sw.toString();
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+      System.out.println("Read the JsonArray back into 'myJsonArray2' using a JsonReader");
+      JsonArray myJsonArray2;
+      try (JsonReader reader = Json.createReader(new StringReader(contents))) {
+        myJsonArray2 = reader.readArray();
+      }
+      System.out.println("Dump contents of JsonArray read from String Contents");
+      JSONP_Util.dumpJsonValue(myJsonArray2);
+
+      System.out.println("Compare myJsonArray1 and myJsonArray2 for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(myJsonArray1, myJsonArray2);
+    } catch (Exception e) {
+      throw new Fault("jsonArrayTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonArrayTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonArrayTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:6; JSONP:JAVADOC:8; JSONP:JAVADOC:10;
+   * JSONP:JAVADOC:430; JSONP:JAVADOC:12; JSONP:JAVADOC:14; JSONP:JAVADOC:16;
+   * JSONP:JAVADOC:18; JSONP:JAVADOC:40; JSONP:JAVADOC:41; JSONP:JAVADOC:42;
+   * JSONP:JAVADOC:44; JSONP:JAVADOC:45; JSONP:JAVADOC:46; JSONP:JAVADOC:48;
+   * JSONP:JAVADOC:49; JSONP:JAVADOC:51; JSONP:JAVADOC:21; JSONP:JAVADOC:25;
+   * JSONP:JAVADOC:101; JSONP:JAVADOC:102; JSONP:JAVADOC:262; JSONP:JAVADOC:263;
+   * JSONP:JAVADOC:400; JSONP:JAVADOC:401; JSONP:JAVADOC:402; JSONP:JAVADOC:403;
+   * JSONP:JAVADOC:404; JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409;
+   * JSONP:JAVADOC:433; JSONP:JAVADOC:434; JSONP:JAVADOC:435; JSONP:JAVADOC:490;
+   * JSONP:JAVADOC:493; JSONP:JAVADOC:496; JSONP:JAVADOC:499; JSONP:JAVADOC:506;
+   * 
+   * @test_Strategy: Tests JsonArray/JsonArrayBuilder API's. Build a JsonArray
+   * using the JsonArrayBuilder API's. Verify contents of JsonArray using
+   * JsonArray().getJsonNumber(int), JsonArray().getJsonString(int),
+   * JsonArray().getJsonArray(int), JsonArray().getJsonObject().
+   *
+   * This also covers testing the following additional API's:
+   *
+   * JsonString.getString(), JsonNumber.bigDecimalValue(),
+   * JsonNumber.bigIntegerValue(), JsonNumber.doubleValue(),
+   * JsonNumber.intValue(), JsonNumber.longValue(), JsonNumber.isIntegral(),
+   * JsonNumber.longValueExact(), JsonNumber.intValueExact(),
+   * JsonNumber.bigIntegerValueExact(), JsonArray.getInt(int),
+   * JsonArray.getString(int), JsonArrau.getBoolean(int),
+   * JsonArray.getBoolean(int, boolean), JsonArray.getInt(int, int),
+   * JsonArray.getString(int, String)
+   */
+  @SuppressWarnings("SuspiciousIndentAfterControlStatement")
+  @Test
+  public void jsonArrayTest3() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      int expInt[] = { -1, 1, 1, -1000, 1000, 1000, -2000, 2000, 2000,
+          Integer.MAX_VALUE, Integer.MIN_VALUE };
+
+      long expLong[] = { Long.MAX_VALUE, Long.MIN_VALUE };
+
+      double expDouble[] = { Double.MAX_VALUE, Double.MIN_VALUE };
+
+      System.out.println("Create myArray Jsonarray of 23 elements");
+      JsonArray myArray = Json.createArrayBuilder() // Indices
+          .add(-1).add(+1).add(1) // 0,1,2
+          .add(-1e3).add(+1e3).add(1e3) // 3,4,5
+          .add(-2E3).add(+2E3).add(2E3) // 6,7,8
+          .add(Integer.MAX_VALUE) // 9
+          .add(Integer.MIN_VALUE) // 10
+          .add(Long.MAX_VALUE) // 11
+          .add(Long.MIN_VALUE) // 12
+          .add(Double.MAX_VALUE) // 13
+          .add(Double.MIN_VALUE) // 14
+          .add(BigDecimal.valueOf(123456789.123456789)) // 15
+          .add(new BigInteger("123456789")) // 16
+          .add(JsonValue.TRUE) // 17
+          .add(JsonValue.FALSE) // 18
+          .add(JsonValue.NULL) // 19
+          .add(JSONP_Data.asciiCharacters) // 20
+          .add(object) // 21
+          .add(array) // 22
+          .build();
+
+      System.out.println("Array size=" + myArray.size());
+
+      // Following array is used to test for Ints that could be one of following
+      // types:
+      boolean expectedIntTypes[] = { JSONP_Util.INTEGRAL,
+          JSONP_Util.NON_INTEGRAL };
+      // Verify JsonValueType=NUMBER and integer value equals expectedIntValue
+      for (int i = 0; i < 11; i++) {
+        System.out.println("Checking getValue(" + i + ") for correctness");
+        System.out.println("Retrieve and verify (JsonValueType=NUMBER)");
+        if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.NUMBER,
+            myArray.getJsonNumber(i).getValueType()))
+          pass = false;
+        System.out.println("Retrieve and (expect JsonNumber NumberType be one of "
+            + JSONP_Util.toStringJsonNumberTypes(expectedIntTypes) + ")");
+        if (!JSONP_Util.assertEqualsJsonNumberTypes(expectedIntTypes,
+            myArray.getJsonNumber(i).isIntegral()))
+          pass = false;
+        System.out.println("Retrieve and verify integer value via JsonNumber.intValue()");
+        if (!JSONP_Util.assertEquals(expInt[i],
+            myArray.getJsonNumber(i).intValue()))
+          pass = false;
+        System.out.println("Retrieve and verify integer value via JsonArray.getInt");
+        if (!JSONP_Util.assertEquals(expInt[i], myArray.getInt(i)))
+          pass = false;
+        System.out.println(
+            "Retrieve and verify integer value via JsonNumber.intValueExact()");
+        if (!JSONP_Util.assertEquals(expInt[i],
+            myArray.getJsonNumber(i).intValueExact()))
+          pass = false;
+      }
+
+      // Verify JsonValueType=NUMBER and long value equals expectedLongValue
+      for (int i = 11, j = 0; i < 13; i++, j++) {
+        System.out.println("Checking getValue(" + i + ") for correctness");
+        System.out.println("Retrieve and verify (JsonValueType=NUMBER)");
+        if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.NUMBER,
+            myArray.getJsonNumber(i).getValueType()))
+          pass = false;
+        System.out.println("Retrieve and (expect JsonNumber NumberType be INTEGRAL)");
+        if (!JSONP_Util.assertEqualsJsonNumberType(JSONP_Util.INTEGRAL,
+            myArray.getJsonNumber(i).isIntegral()))
+          pass = false;
+        System.out.println("Retrieve and verify long value via JsonNumber.longValue()");
+        if (!JSONP_Util.assertEquals(expLong[j],
+            myArray.getJsonNumber(i).longValue()))
+          pass = false;
+        System.out.println(
+            "Retrieve and verify long value via JsonNumber.longValueExact()");
+        if (!JSONP_Util.assertEquals(expLong[j],
+            myArray.getJsonNumber(i).longValueExact()))
+          pass = false;
+      }
+
+      // Following array is used to test for Doubles that could be one of
+      // following types:
+      boolean expectedDoubleTypes[] = { JSONP_Util.INTEGRAL,
+          JSONP_Util.NON_INTEGRAL };
+
+      // Verify JsonValueType=NUMBER and double value equals expectedDoubleValue
+      for (int i = 13, j = 0; i < 15; i++, j++) {
+        System.out.println("Checking getValue(" + i + ") for correctness");
+        System.out.println("Retrieve and verify (JsonValueType=NUMBER)");
+        if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.NUMBER,
+            myArray.getJsonNumber(i).getValueType()))
+          pass = false;
+        System.out.println("Retrieve and (expect JsonNumber NumberType be one of "
+            + JSONP_Util.toStringJsonNumberTypes(expectedDoubleTypes) + ")");
+        if (!JSONP_Util.assertEqualsJsonNumberTypes(expectedDoubleTypes,
+            myArray.getJsonNumber(i).isIntegral()))
+          pass = false;
+        System.out.println("Retrieve and verify double value via JsonNumber.doubleValue()");
+        if (!JSONP_Util.assertEquals(expDouble[j],
+            myArray.getJsonNumber(i).doubleValue()))
+          pass = false;
+      }
+
+      // Verify JsonValueType=NUMBER and BigDecimalValue equals
+      // expectedBigDecimal
+      System.out.println("Checking getValue(15) for correctness");
+      System.out.println("Retrieve and verify (JsonValueType=NUMBER)");
+      if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.NUMBER,
+          myArray.getJsonNumber(15).getValueType()))
+        pass = false;
+      System.out.println("Retrieve and (expect JsonNumber NumberType be one of "
+          + JSONP_Util.toStringJsonNumberTypes(expectedDoubleTypes) + ")");
+      if (!JSONP_Util.assertEqualsJsonNumberTypes(expectedDoubleTypes,
+          myArray.getJsonNumber(15).isIntegral()))
+        pass = false;
+      System.out.println(
+          "Retrieve and verify BigDecimal value via JsonNumber.bigDecimalValue()");
+      if (!JSONP_Util.assertEquals(BigDecimal.valueOf(123456789.123456789),
+          myArray.getJsonNumber(15).bigDecimalValue()))
+        pass = false;
+
+      // Verify JsonValueType=NUMBER and BigIntegerValue equals
+      // expectedBigInteger
+      System.out.println("Checking getValue(16) for correctness");
+      System.out.println("Retrieve and verify (JsonValueType=NUMBER)");
+      if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.NUMBER,
+          myArray.getJsonNumber(16).getValueType()))
+        pass = false;
+      System.out.println("Retrieve and (expect JsonNumber NumberType be INTEGRAL)");
+      if (!JSONP_Util.assertEqualsJsonNumberType(JSONP_Util.INTEGRAL,
+          myArray.getJsonNumber(16).isIntegral()))
+        pass = false;
+      System.out.println(
+          "Retrieve and verify BigInteger value via JsonNumber.bigIntegerValue()");
+      if (!JSONP_Util.assertEquals(new BigInteger("123456789"),
+          myArray.getJsonNumber(16).bigIntegerValue()))
+        pass = false;
+      System.out.println(
+          "Retrieve and verify BigInteger value via JsonNumber.bigIntegerValueExact()");
+      if (!JSONP_Util.assertEquals(new BigInteger("123456789"),
+          myArray.getJsonNumber(16).bigIntegerValueExact()))
+        pass = false;
+
+      // Verify getBoolean(int)=true
+      System.out.println("Retrieve and verify true value via JsonArray.getBoolean(int)");
+      if (!JSONP_Util.assertEquals(true, myArray.getBoolean(17)))
+        pass = false;
+
+      // Verify getBoolean(int)=false
+      System.out.println("Retrieve and verify false value via JsonArray.getBoolean(int)");
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(18)))
+        pass = false;
+
+      // Verify isNull(int)=true
+      System.out.println("Retrieve and verify null value via JsonArray.isNull(int)");
+      if (!JSONP_Util.assertEquals(true, myArray.isNull(19)))
+        pass = false;
+
+      // Verify isNull(int)=false
+      System.out.println("Retrieve and verify non-null value via JsonArray.isNull(int)");
+      if (!JSONP_Util.assertEquals(false, myArray.isNull(20)))
+        pass = false;
+
+      // Verify JsonValueType=STRING and getJsonString()=expectedString
+      System.out.println("Checking getValue(20) for correctness");
+      System.out.println("Retrieve and (expect JsonValueType=STRING)");
+      if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.STRING,
+          myArray.getJsonString(20).getValueType()))
+        pass = false;
+      System.out.println("Retrieve and verify string value via JsonString.getString()");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myArray.getJsonString(20).getString()))
+        pass = false;
+      System.out.println("Retrieve and verify string value via JsonArray.getString()");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myArray.getString(20)))
+        pass = false;
+
+      // Verify JsonValueType=OBJECT and getJsonObject()=expectedObject
+      System.out.println("Checking getJsonObject(21) for correctness");
+      System.out.println("Retrieve and (expect JsonValueType=OBJECT)");
+      if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.OBJECT,
+          myArray.getJsonObject(21).getValueType()))
+        pass = false;
+      System.out.println(
+          "Retrieve and verify object value via JsonArray.getJsonObject(int)");
+      if (!JSONP_Util.assertEqualsJsonObjects(object,
+          myArray.getJsonObject(21)))
+        pass = false;
+
+      // Verify JsonValueType=ARRAY and getJsonArray()=expectedArray
+      System.out.println("Checking getJsonArray(22) for correctness");
+      System.out.println("Retrieve and (expect JsonValueType=ARRAY)");
+      if (!JSONP_Util.assertEqualsJsonValueType(JsonValue.ValueType.ARRAY,
+          myArray.getJsonArray(22).getValueType()))
+        pass = false;
+      System.out.println("Retrieve and verify array value via JsonArray.getJsonArray(int)");
+      if (!JSONP_Util.assertEqualsJsonArrays(array, myArray.getJsonArray(22)))
+        pass = false;
+
+      // Verify calls to JsonArray.getBoolean(int)
+      if (!JSONP_Util.assertEquals(true, myArray.getBoolean(17)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(18)))
+        pass = false;
+
+      // Verify calls to JsonArray.getBoolean(int, boolean)
+      System.out.println(
+          "Testing JsonArray.getBoolean(int, boolean) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(true, myArray.getBoolean(17, false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(0, false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(19, false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(20, false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(21, false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myArray.getBoolean(22, false)))
+        pass = false;
+
+      // Verify calls to JsonArray.getInt(int, int)
+      System.out.println(
+          "Testing JsonArray.getInt(int, int) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(-1, myArray.getInt(0, 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myArray.getInt(17, 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myArray.getInt(19, 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myArray.getInt(20, 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myArray.getInt(21, 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myArray.getInt(22, 10)))
+        pass = false;
+
+      // Verify calls to JsonArray.getString(int, String)
+      System.out.println(
+          "Testing JsonArray.getString(int, String) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myArray.getString(20, "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myArray.getString(17, "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myArray.getString(19, "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myArray.getString(2, "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myArray.getString(21, "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myArray.getString(22, "foo")))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonArrayTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonArrayTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonArrayTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:6; JSONP:JAVADOC:8; JSONP:JAVADOC:14;
+   * JSONP:JAVADOC:16; JSONP:JAVADOC:18; JSONP:JAVADOC:21; JSONP:JAVADOC:25;
+   * JSONP:JAVADOC:106; JSONP:JAVADOC:400; JSONP:JAVADOC:401; JSONP:JAVADOC:402;
+   * JSONP:JAVADOC:403; JSONP:JAVADOC:404; JSONP:JAVADOC:406; JSONP:JAVADOC:409;
+   * 
+   * @test_Strategy: Build a JsonArray and than write the JsonArray. Compare the
+   * Json text from the writer contents with the expected Json text output
+   * expected based on the JsonArray.
+   */
+  @Test
+  public void jsonArrayTest4() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      StringWriter sw = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sw)) {
+        writer.writeArray(myJsonArray1);
+        System.out.println("Close JsonWriter");
+      }
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sw.toString();
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+      System.out.println("Remove whitespace from contents.");
+      String actJsonText = JSONP_Util.removeWhitespace(contents);
+      System.out.println(
+          "Compare expected JsonArray text with actual JsonArray text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONARRAY_TEXT, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("jsonArrayTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonArrayTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonArrayGetValuesAsTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:403; JSONP:JAVADOC:481; JSONP:JAVADOC:8;
+   * JSONP:JAVADOC:14;
+   * 
+   * @test_Strategy: Build a Json array of values of the same type. Get the
+   * values as a list for that type. Compare results in list to what is expected
+   * for equality.
+   *
+   * APIs called: JsonArray array = Json.createArrayBuilder().add(...).build()
+   * List<T> JsonArray.getValuesAs(Class)
+   */
+  @SuppressWarnings("SuspiciousIndentAfterControlStatement")
+  @Test
+  public void jsonArrayGetValuesAsTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonArray of JsonNumber types for testing");
+      JsonArray jsonArr = Json.createArrayBuilder().add(100).add(500).build();
+
+      System.out.println("Create the expected list of JsonArray values");
+      List<JsonValue> expList = new ArrayList<>();
+      expList.add(JSONP_Util.createJsonNumber(100));
+      expList.add(JSONP_Util.createJsonNumber(500));
+      JSONP_Util.dumpList(expList, "Expected List");
+
+      System.out.println("Create the JsonNumber list of JsonArray values");
+      List<JsonNumber> numList = jsonArr.getValuesAs(JsonNumber.class);
+
+      System.out.println("Create the actual list of JsonArray values");
+      List<JsonValue> actList = new ArrayList<>();
+      for (JsonNumber num : numList)
+        actList.add(num);
+
+      System.out.println("Compare actual list with expected list for equality");
+      pass = JSONP_Util.assertEqualsList(expList, actList);
+
+      System.out.println("Create sample JsonArray of JsonString types for testing");
+      jsonArr = Json.createArrayBuilder().add("hello").add("world").build();
+
+      System.out.println("Create the list of JsonString values");
+      List<JsonString> strList = jsonArr.getValuesAs(JsonString.class);
+
+      System.out.println("Comparing JsonString list elements to expected values.");
+      if (!JSONP_Util.assertEquals(jsonArr.getString(0),
+          strList.get(0).getString()))
+        pass = false;
+
+      if (!JSONP_Util.assertEquals(jsonArr.getString(1),
+          strList.get(1).getString()))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonArrayGetValuesAsTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonArrayGetValuesAsTest Failed");
+  }
+
+  /*
+   * @testName: jsonArrayExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:43; JSONP:JAVADOC:47; JSONP:JAVADOC:50;
+   * JSONP:JAVADOC:20; JSONP:JAVADOC:377; JSONP:JAVADOC:432; JSONP:JAVADOC:379;
+   * JSONP:JAVADOC:378; JSONP:JAVADOC:380; JSONP:JAVADOC:431; JSONP:JAVADOC:400;
+   * JSONP:JAVADOC:401; JSONP:JAVADOC:402; JSONP:JAVADOC:403; JSONP:JAVADOC:404;
+   * JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409; JSONP:JAVADOC:491;
+   * JSONP:JAVADOC:492; JSONP:JAVADOC:494; JSONP:JAVADOC:495; JSONP:JAVADOC:497;
+   * JSONP:JAVADOC:498; JSONP:JAVADOC:500; JSONP:JAVADOC:501; JSONP:JAVADOC:507;
+   * 
+   * @test_Strategy: Test JsonArray exception conditions. Trips the exceptions:
+   * java.lang.IndexOutOfBoundsException java.lang.ArithmeticException
+   * java.lang.NumberFormatException java.lang.ClassCastException
+   * java.lang.UnsupportedOperationException
+   */
+  @Test
+  public void jsonArrayExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonObject testObject = null;
+    JsonArray testArray = null;
+
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      testObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      testArray = JSONP_Util.createSampleJsonArray();
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonNumber via getJsonNumber(int)");
+      JsonNumber value = testArray.getJsonNumber(0);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonNumber via getJsonNumber(int)");
+      JsonNumber value = testArray.getJsonNumber(15);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonNumber to JsonString via getJsonString(int)");
+      JsonString value = testArray.getJsonString(4);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to JsonNumber via getJsonNumber(int)");
+      JsonNumber value = testArray.getJsonNumber(6);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonValue.TRUE to JsonNumber via getJsonNumber(int)");
+      JsonNumber value = testArray.getJsonNumber(1);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonArray via getJsonArray(int)");
+      JsonArray value = testArray.getJsonArray(0);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonObject via getJsonObject(int)");
+      JsonObject value = testArray.getJsonObject(15);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonNumber via getInt(int)");
+      int value = testArray.getInt(0);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonString via getString(int)");
+      String value = testArray.getString(0);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonString via getString(int)");
+      String value = testArray.getString(15);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to boolean via getBoolean(int)");
+      boolean value = testArray.getBoolean(0);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to boolean via getBoolean(int)");
+      boolean value = testArray.getBoolean(13);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to boolean via getBoolean(int)");
+      boolean value = testArray.getBoolean(6);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonNumber to boolean via getBoolean(int)");
+      boolean value = testArray.getBoolean(4);
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getJsonNumber(int)");
+      int myInt = testArray.getJsonNumber(-1).intValue();
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getJsonNumber(int)");
+      JsonValue myJsonValue = testArray.getJsonNumber(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getJsonArray(int)");
+      JsonValue myJsonValue = testArray.getJsonArray(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getJsonArray(int)");
+      JsonValue myJsonValue = testArray.getJsonArray(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getJsonObject(int)");
+      JsonValue myJsonValue = testArray.getJsonObject(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getJsonObject(int)");
+      JsonValue myJsonValue = testArray.getJsonObject(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getJsonString(int)");
+      JsonValue myJsonValue = testArray.getJsonString(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getJsonString(int)");
+      JsonValue myJsonValue = testArray.getJsonString(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getInt(int)");
+      int myInt = testArray.getInt(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getInt(int)");
+      int myInt = testArray.getInt(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getString(int)");
+      String myString = testArray.getString(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getString(int)");
+      String myString = testArray.getString(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to getBoolean(int)");
+      boolean myBoolean = testArray.getBoolean(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to getBoolean(int)");
+      boolean myBoolean = testArray.getBoolean(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing -1 as index to isNull(int)");
+      boolean myBoolean = testArray.isNull(-1);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip IndexOutOfBoundsException
+    try {
+      System.out.println(
+          "Trip IndexOutOfBoundsException passing 10000 as index to isNull(int)");
+      boolean myBoolean = testArray.isNull(10000);
+      pass = false;
+      System.err.println("Failed to throw IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException e) {
+      System.out.println("Got expected IndexOutOfBoundsException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NumberFormatException calling add(Double.NaN)
+    try {
+      System.out.println("Trip NumberFormatException calling add(Double.NaN)");
+      JsonArray array = Json.createArrayBuilder().add(Double.NaN).build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NumberFormatException calling add(Double.NEGATIVE_INFINITY)
+    try {
+      System.out.println(
+          "Trip NumberFormatException calling add(Double.NEGATIVE_INFINITY)");
+      JsonArray array = Json.createArrayBuilder().add(Double.NEGATIVE_INFINITY)
+          .build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NumberFormatException calling add(Double.POSITIVE_INFINITY)
+    try {
+      System.out.println(
+          "Trip NumberFormatException calling add(Double.POSITIVE_INFINITY)");
+      JsonArray array = Json.createArrayBuilder().add(Double.POSITIVE_INFINITY)
+          .build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(12345.12345) and attempting to extract as an exact integer value");
+      JsonArray array = Json.createArrayBuilder().add(12345.12345).build();
+      System.out.println("Call JsonArray.getJsonNumber(0).intValueExact()");
+      int value = array.getJsonNumber(0).intValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(12345.12345) and attempting to extract as an exact long value");
+      JsonArray array = Json.createArrayBuilder().add(12345.12345).build();
+      System.out.println("Call JsonArray.getJsonNumber(0).longValueExact()");
+      long value = array.getJsonNumber(0).longValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(12345.12345) and attempting to extract as an exact biginteger value");
+      JsonArray array = Json.createArrayBuilder().add(12345.12345).build();
+      System.out.println("Call JsonArray.getJsonNumber(0).bigIntegerValueExact()");
+      BigInteger value = array.getJsonNumber(0).bigIntegerValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Tests for UnsupportedOperationException using Collection methods to
+    // modify JsonArray List
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.add(E) trying to modify JsonArray list which should be immutable");
+      testArray.add(JsonValue.FALSE);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.add(int,E) trying to modify JsonArray list which should be immutable");
+      testArray.add(0, JsonValue.FALSE);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.addAll(C) trying to modify JsonArray list which should be immutable");
+      testArray.addAll(testArray);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.addAll(int, C) trying to modify JsonArray list which should be immutable");
+      testArray.addAll(0, testArray);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.clear() trying to modify JsonArray list which should be immutable");
+      testArray.clear();
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.remove(int) trying to modify JsonArray list which should be immutable");
+      testArray.remove(0);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonArray.removeAll(C) trying to modify JsonArray list which should be immutable");
+      testArray.removeAll(testArray);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException trying to modify JsonArray list which should be immutable");
+      testArray.remove(JsonValue.TRUE);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonArrayExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonArrayNullValueExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:555; JSONP:JAVADOC:556; JSONP:JAVADOC:557;
+   * JSONP:JAVADOC:558; JSONP:JAVADOC:559; JSONP:JAVADOC:560;
+   * 
+   * @test_Strategy: Test JSON NPE exception conditions when attempting to add a
+   * specified value that is null.
+   */
+  @Test
+  public void jsonArrayNullValueExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonArrayBuilder jab = Json.createArrayBuilder();
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(JsonValue) when JsonValue is null.");
+      jab.add((JsonValue) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(BigInteger) when BigInteger is null.");
+      jab.add((BigInteger) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(JsonArrayBuilder) when JsonArrayBuilder is null.");
+      jab.add((JsonArrayBuilder) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(JsonObjectBuilder) when JsonObjectBuilder is null.");
+      jab.add((JsonObjectBuilder) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(BigDecimal) when BigDecimal is null.");
+      jab.add((BigDecimal) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(String) when String is null.");
+      jab.add((String) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonArrayNullValueExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonCreateArrayBuilder11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:572; JSONP:JAVADOC:573; JSONP:JAVADOC:651;
+   * JSONP:JAVADOC:652;
+   *
+   * @test_Strategy: Tests JsonArrayBuilder API factory methods added in JSON-P
+   * 1.1.
+   */
+  @Test
+  public void jsonCreateArrayBuilder11Test() throws Fault {
+    ArrayBuilders createTest = new ArrayBuilders();
+    final TestResult result = createTest.test();
+    result.eval();
+  }
+
+  /*
+   * @testName: jsonArrayBuilder11AddTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:589; JSONP:JAVADOC:590; JSONP:JAVADOC:591;
+   * JSONP:JAVADOC:592; JSONP:JAVADOC:593; JSONP:JAVADOC:594; JSONP:JAVADOC:595;
+   * JSONP:JAVADOC:596; JSONP:JAVADOC:597; JSONP:JAVADOC:598; JSONP:JAVADOC:599;
+   * JSONP:JAVADOC:600; JSONP:JAVADOC:601;
+   *
+   * @test_Strategy: Tests JsonArrayBuilder API add() methods added in JSON-P
+   * 1.1.
+   */
+  @Test
+  public void jsonArrayBuilder11AddTest() throws Fault {
+    ArrayBuildAdd addTest = new ArrayBuildAdd();
+    final TestResult result = addTest.test();
+    result.eval();
+  }
+
+  /*
+   * @testName: jsonArrayBuilder11SetTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:603; JSONP:JAVADOC:604; JSONP:JAVADOC:605;
+   * JSONP:JAVADOC:606; JSONP:JAVADOC:607; JSONP:JAVADOC:608; JSONP:JAVADOC:609;
+   * JSONP:JAVADOC:610; JSONP:JAVADOC:611; JSONP:JAVADOC:612; JSONP:JAVADOC:613;
+   * 
+   * @test_Strategy: Tests JsonArrayBuilder API set() methods added in JSON-P
+   * 1.1.
+   */
+  @Test
+  public void jsonArrayBuilder11SetTest() throws Fault {
+    ArrayBuildSet setTest = new ArrayBuildSet();
+    final TestResult result = setTest.test();
+    result.eval();
+  }
+
+  /*
+   * @testName: jsonArrayBuilder11RemoveTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:602;
+   * 
+   * @test_Strategy: Tests JsonArrayBuilder API remove() methods added in JSON-P
+   * 1.1.
+   */
+  @Test
+  public void jsonArrayBuilder11RemoveTest() throws Fault {
+    ArrayBuildRemove removeTest = new ArrayBuildRemove();
+    final TestResult result = removeTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/BuilderFactory.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/BuilderFactory.java
new file mode 100644
index 0000000..0ba5076
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/BuilderFactory.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonbuilderfactorytests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonBuilderFactory;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for
+ * {@link JsonBuilderFactory}.
+ */
+public class BuilderFactory {
+
+
+  /**
+   * {@link JsonBuilderFactory} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonBuilderFactory API methods added in JSON-P 1.1.");
+    System.out.println("JsonBuilderFactory API methods added in JSON-P 1.1.");
+    testCreateArrayBuilderString(result);
+    testCreateArrayBuilderInt(result);
+    testCreateArrayBuilderBool(result);
+    testCreateArrayBuilderObject(result);
+    testCreateArrayBuilderNull(result);
+    testCreateObjectBuilderString(result);
+    testCreateObjectBuilderInt(result);
+    testCreateObjectBuilderBool(result);
+    testCreateObjectBuilderObject(result);
+    testCreateObjectBuilderNull(result);
+    return result;
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder createArrayBuilder(JsonArray)} method on
+   * {@code String} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateArrayBuilderString(final TestResult result) {
+    System.out.println(" - createArrayBuilder(JsonArray) for String");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createStringArray2();
+    verifyCreateArrayBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder createArrayBuilder(JsonArray)} method on
+   * {@code int} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateArrayBuilderInt(final TestResult result) {
+    System.out.println(" - createArrayBuilder(JsonArray) for int");
+    final JsonArray in = createIntArray2();
+    final JsonArray check = createIntArray2();
+    verifyCreateArrayBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder createArrayBuilder(JsonArray)} method on
+   * {@code boolean} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateArrayBuilderBool(final TestResult result) {
+    System.out.println(" - createArrayBuilder(JsonArray) for boolean");
+    final JsonArray in = createBoolArray2();
+    final JsonArray check = createBoolArray2();
+    verifyCreateArrayBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder createArrayBuilder(JsonArray)} method on
+   * {@code JsonObject} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateArrayBuilderObject(final TestResult result) {
+    System.out.println(" - createArrayBuilder(JsonArray) for JsonObject");
+    final JsonArray in = createObjectArray2();
+    final JsonArray check = createObjectArray2();
+    verifyCreateArrayBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder createArrayBuilder(JsonArray)} method on
+   * {@code null} value. Method shall throw NullPointerException.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateArrayBuilderNull(final TestResult result) {
+    System.out.println(" - createArrayBuilder(JsonArray) for null");
+    final JsonArray in = null;
+    final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+    try {
+      factory.createArrayBuilder(in);
+      result.fail("createArrayBuilder(JsonArray)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println(
+          "    - Expected exception for null argument: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("createObjectBuilder(JsonObject)",
+          "Calling method with with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder createObjectBuilder(JsonObject)} method on
+   * {@code String} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateObjectBuilderString(final TestResult result) {
+    System.out.println(" - createObjectBuilder(JsonObject) for String");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectStr();
+    verifyCreateObjectBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder createObjectBuilder(JsonObject)} method on
+   * {@code int} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateObjectBuilderInt(final TestResult result) {
+    System.out.println(" - createObjectBuilder(JsonObject) for int");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectInt();
+    verifyCreateObjectBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder createObjectBuilder(JsonObject)} method on
+   * {@code boolean} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateObjectBuilderBool(final TestResult result) {
+    System.out.println(" - createObjectBuilder(JsonObject) for boolean");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectBool();
+    verifyCreateObjectBuilder(result, check, in);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder createObjectBuilder(JsonObject)} method on
+   * {@code JsonObject} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateObjectBuilderObject(final TestResult result) {
+    System.out.println(" - createObjectBuilder(JsonObject) for JsonObject");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject check = createSimpleObjectObject();
+    verifyCreateObjectBuilder(result, check, in);
+  }
+
+  /**
+   * Test helper: Verify {@code JsonArrayBuilder createArrayBuilder(JsonArray)}
+   * method on provided array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param in
+   *          JSON array to pass to the method.
+   */
+  private void verifyCreateArrayBuilder(final TestResult result,
+      final JsonArray check, final JsonArray in) {
+    final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+    final JsonArrayBuilder builder = factory.createArrayBuilder(in);
+    final JsonArray out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("createArrayBuilder(JsonArray)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test helper: Verify
+   * {@code JsonObjectBuilder createObjectBuilder(JsonObjecty)} method on
+   * provided object.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param in
+   *          JSON object to pass to the method.
+   */
+  private void verifyCreateObjectBuilder(final TestResult result,
+      final JsonObject check, final JsonObject in) {
+    System.out.println("    - IN: " + valueToString(in));
+    final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+    final JsonObjectBuilder builder = factory.createObjectBuilder(in);
+    final JsonObject out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("createObjectBuilder(JsonObject)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder createObjectBuilder(JsonObject)} method on
+   * {@code null} value. Method shall throw NullPointerException.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateObjectBuilderNull(final TestResult result) {
+    System.out.println(" - createObjectBuilder(JsonObject) for null");
+    final JsonObject in = null;
+    final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+    try {
+      factory.createObjectBuilder(in);
+      result.fail("createObjectBuilder(JsonObject)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println(
+          "    - Expected exception for null argument: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("createObjectBuilder(JsonObject)",
+          "Calling method with with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/ClientTests.java
new file mode 100644
index 0000000..94ad80c
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonbuilderfactorytests/ClientTests.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonbuilderfactorytests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonBuilderFactoryTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:445; JSONP:JAVADOC:453; JSONP:JAVADOC:454;
+   * JSONP:JAVADOC:455;
+   * 
+   * @test_Strategy: Tests the JsonBuilderFactory API.
+   *
+   * JsonBuilderFactory builderFactory = Json.createBuilderFactory(Map<String,
+   * ?>); JsonArray array = builderFactory.createArrayBuilder() JsonObject
+   * object = builderFactory.createObjectBuilder()
+   */
+  @Test
+  public void jsonBuilderFactoryTest1() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create JsonBuilderFactory with Map<String, ?> with EMPTY config");
+      JsonBuilderFactory builderFactory = Json
+          .createBuilderFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = builderFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("---------------------------------------------------");
+      System.out.println("TEST CASE [JsonBuilderFactory.createArrayBuilder()]");
+      System.out.println("---------------------------------------------------");
+      System.out.println("Create JsonArrayBuilder using JsonBuilderFactory");
+      JsonArray expJsonArray = JSONP_Util.createJsonArrayFromString("[0,2]");
+      JsonArray actJsonArray = builderFactory.createArrayBuilder().add(0).add(2)
+          .build();
+      if (!JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray))
+        pass = false;
+
+      System.out.println("----------------------------------------------------");
+      System.out.println("TEST CASE [JsonBuilderFactory.createObjectBuilder()]");
+      System.out.println("----------------------------------------------------");
+      System.out.println("Create JsonObjectBuilder using JsonBuilderFactory");
+      JsonObject expJsonObject = JSONP_Util
+          .createJsonObjectFromString("{\"foo\":\"bar\"}");
+      JsonObject actJsonObject = builderFactory.createObjectBuilder()
+          .add("foo", "bar").build();
+      if (!JSONP_Util.assertEqualsJsonObjects(expJsonObject, actJsonObject))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonBuilderFactoryTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonBuilderFactoryTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonBuilderFactoryTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:445; JSONP:JAVADOC:455;
+   * 
+   * @test_Strategy: Tests the JsonBuilderFactory API.
+   *
+   * JsonBuilderFactory builderFactory = Json.createBuilderFactory(Map<String,
+   * ?>); Map<String, ?> config = JsonBuilderFactory.getConfigInUse();
+   *
+   * Test for the following 3 scenarios: 1) no supported provider property
+   * (empty config) 2) non supported provider property
+   */
+  @Test
+  public void jsonBuilderFactoryTest2() throws Fault {
+    boolean pass = true;
+    JsonBuilderFactory builderFactory;
+    Map<String, ?> config;
+    try {
+      System.out.println("----------------------------------------------");
+      System.out.println("Test scenario1: no supported provider property");
+      System.out.println("----------------------------------------------");
+      System.out.println("Create JsonBuilderFactory with Map<String, ?> with EMPTY config");
+      builderFactory = Json.createBuilderFactory(JSONP_Util.getEmptyConfig());
+      config = builderFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-----------------------------------------------");
+      System.out.println("Test scenario2: non supported provider property");
+      System.out.println("-----------------------------------------------");
+      System.out.println("Create JsonBuilderFactory with Map<String, ?> with FOO config");
+      builderFactory = Json.createBuilderFactory(JSONP_Util.getFooConfig());
+      config = builderFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonBuilderFactoryTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonBuilderFactoryTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonBuilderFactory11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:614; JSONP:JAVADOC:615;
+   * 
+   * @test_Strategy: Tests JsonBuilderFactory API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonBuilderFactory11Test() throws Fault {
+    BuilderFactory factoryTest = new BuilderFactory();
+    final TestResult result = factoryTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsoncoding/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsoncoding/ClientTests.java
new file mode 100644
index 0000000..cecc832
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsoncoding/ClientTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsoncoding;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.Json;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonEncodeTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:681; JSONP:JAVADOC:682;
+   * 
+   * @test_Strategy: Encode and decode Json Pointer as defined by RFC 6901
+   */
+  @Test
+  public void jsonEncodeTest() throws Fault {
+    String DECODED = "/a/~b/c";
+    String ENCODED = "~1a~1~0b~1c";
+    StringBuilder error = new StringBuilder();
+    System.out.println("----------------------------------------------");
+    System.out.println("Test encode " + DECODED);
+    System.out.println("----------------------------------------------");
+    String encoded = Json.encodePointer(DECODED);
+    if (!ENCODED.equals(encoded))
+      error.append("The pointer ").append(DECODED)
+          .append(" has been encoded as ").append(encoded).append('\n');
+
+    System.out.println("----------------------------------------------");
+    System.out.println("Test decode " + ENCODED);
+    String decoded = Json.decodePointer(ENCODED);
+    if (!DECODED.equals(decoded))
+      error.append("The pointer ").append(ENCODED)
+          .append(" has been decoded as ").append(decoded).append('\n');
+    if (error.length() != 0)
+      throw new Fault(error.toString());
+    System.out.println("----------------------------------------------");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratorfactorytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratorfactorytests/ClientTests.java
new file mode 100644
index 0000000..af39a34
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratorfactorytests/ClientTests.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsongeneratorfactorytests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ArrayList;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonGeneratorFactoryTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:162; JSONP:JAVADOC:416; JSONP:JAVADOC:427;
+   * 
+   * @test_Strategy: Tests the JsonGeneratorFactory API.
+   *
+   * JsonGeneratorFactory generatorFactory =
+   * Json.createGeneratorFactory(Map<String, ?>); JsonGenerator generator1 =
+   * generatorFactory.createGenerator(Writer) JsonGenerator generator2 =
+   * generatorFactory.createGenerator(Writer)
+   */
+  @Test
+  public void jsonGeneratorFactoryTest1() throws Fault {
+    boolean pass = true;
+    JsonGenerator generator1 = null;
+    JsonGenerator generator2 = null;
+    String expString;
+    String actString;
+    try {
+      System.out.println(
+          "Create JsonGeneratorFactory with Map<String, ?> with PRETTY_PRINTING config");
+      JsonGeneratorFactory generatorFactory = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = generatorFactory.getConfigInUse();
+      String[] props = { JsonGenerator.PRETTY_PRINTING, };
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+      System.out.println("--------------------------------------------------------");
+      System.out.println("TEST CASE [JsonGeneratorFactory.createGenerator(Writer)]");
+      System.out.println("--------------------------------------------------------");
+      System.out.println("Create 1st JsonGenerator using JsonGeneratorFactory");
+      StringWriter sWriter1 = new StringWriter();
+      generator1 = generatorFactory.createGenerator(sWriter1);
+      if (generator1 == null) {
+        System.err.println("GeneratorFactory failed to create generator1");
+        pass = false;
+      } else {
+        generator1.writeStartObject().writeEnd();
+        generator1.close();
+      }
+      System.out.println("sWriter1=" + sWriter1.toString());
+      expString = "{}";
+      actString = JSONP_Util.removeWhitespace(sWriter1.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println("Create 2nd JsonGenerator using JsonGeneratorFactory");
+      StringWriter sWriter2 = new StringWriter();
+      generator2 = generatorFactory.createGenerator(sWriter2);
+      if (generator2 == null) {
+        System.err.println("GeneratorFactory failed to create generator2");
+        pass = false;
+      } else {
+        generator2.writeStartArray().writeEnd();
+        generator2.close();
+      }
+      System.out.println("sWriter2=" + sWriter2.toString());
+      expString = "[]";
+      actString = JSONP_Util.removeWhitespace(sWriter2.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorFactoryTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorFactoryTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorFactoryTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:163; JSONP:JAVADOC:416; JSONP:JAVADOC:427;
+   * 
+   * @test_Strategy: Tests the JsonGeneratorFactory API.
+   *
+   * JsonGeneratorFactory generatorFactory =
+   * Json.createGeneratorFactory(Map<String,?>); JsonGenerator generator1 =
+   * generatorFactory.createGenerator(OutputStream, Charset) JsonGenerator
+   * generator2 = generatorFactory.createGenerator(OutputStream, Charset)
+   *
+   * Create generator with both UTF-8 and UTF-16BE.
+   */
+  @Test
+  public void jsonGeneratorFactoryTest2() throws Fault {
+    boolean pass = true;
+    JsonGenerator generator1 = null;
+    JsonGenerator generator2 = null;
+    String expString, actString;
+    try {
+      System.out.println(
+          "Create JsonGeneratorFactory with Map<String, ?> with PRETTY_PRINTING config");
+      JsonGeneratorFactory generatorFactory = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = generatorFactory.getConfigInUse();
+      String[] props = { JsonGenerator.PRETTY_PRINTING, };
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+
+      System.out.println(
+          "-----------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [JsonGeneratorFactory.createGenerator(OutputStream, Charset)]");
+      System.out.println(
+          "-----------------------------------------------------------------------");
+      System.out.println(
+          "Create 1st JsonGenerator using JsonGeneratorFactory with UTF-8 encoding");
+      ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+      generator1 = generatorFactory.createGenerator(baos1, JSONP_Util.UTF_8);
+      if (generator1 == null) {
+        System.err.println("GeneratorFactory failed to create generator1");
+        pass = false;
+      } else {
+        generator1.writeStartObject().writeEnd();
+        generator1.close();
+      }
+      System.out.println("baos1=" + baos1.toString("UTF-8"));
+      expString = "{}";
+      actString = JSONP_Util.removeWhitespace(baos1.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println(
+          "Create 2nd JsonGenerator using JsonGeneratorFactory with UTF-16BE encoding");
+      ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+      generator2 = generatorFactory.createGenerator(baos2, JSONP_Util.UTF_16BE);
+      if (generator2 == null) {
+        System.err.println("GeneratorFactory failed to create generator2");
+        pass = false;
+      } else {
+        generator2.writeStartArray().writeEnd();
+        generator2.close();
+      }
+      System.out.println("baos2=" + baos2.toString("UTF-16BE"));
+      expString = "[]";
+      actString = JSONP_Util.removeWhitespace(baos2.toString("UTF-16BE"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorFactoryTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorFactoryTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorFactoryTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:200; JSONP:JAVADOC:416; JSONP:JAVADOC:427;
+   * 
+   * @test_Strategy: Tests the JsonGeneratorFactory API.
+   *
+   * JsonGeneratorFactory generatorFactory =
+   * Json.createGeneratorFactory(Map<String, ?>); JsonGenerator generator1 =
+   * generatorFactory.createGenerator(OutputStream) JsonGenerator generator2 =
+   * generatorFactory.createGenerator(OutputStream)
+   */
+  @Test
+  public void jsonGeneratorFactoryTest3() throws Fault {
+    boolean pass = true;
+    JsonGenerator generator1 = null;
+    JsonGenerator generator2 = null;
+    String expString;
+    String actString;
+    try {
+      System.out.println(
+          "Create JsonGeneratorFactory with Map<String, ?> with PRETTY_PRINTING config");
+      JsonGeneratorFactory generatorFactory = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = generatorFactory.getConfigInUse();
+      String[] props = { JsonGenerator.PRETTY_PRINTING, };
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+      System.out.println(
+          "-----------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [JsonGeneratorFactory.createGenerator(OutputStream os)]");
+      System.out.println(
+          "-----------------------------------------------------------------");
+      System.out.println("Create 1st JsonGenerator using JsonGeneratorFactory");
+      ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+      generator1 = generatorFactory.createGenerator(baos1);
+      if (generator1 == null) {
+        System.err.println("GeneratorFactory failed to create generator1");
+        pass = false;
+      } else {
+        generator1.writeStartObject().writeEnd();
+        generator1.close();
+      }
+      System.out.println("baos1=" + baos1.toString("UTF-8"));
+      expString = "{}";
+      actString = JSONP_Util.removeWhitespace(baos1.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println("Create 2nd JsonGenerator using JsonGeneratorFactory");
+      ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+      generator2 = generatorFactory.createGenerator(baos2);
+      if (generator2 == null) {
+        System.err.println("GeneratorFactory failed to create generator2");
+        pass = false;
+      } else {
+        generator2.writeStartArray().writeEnd();
+        generator2.close();
+      }
+      System.out.println("baos2=" + baos2.toString("UTF-8"));
+      expString = "[]";
+      actString = JSONP_Util.removeWhitespace(baos2.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorFactoryTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorFactoryTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorFactoryTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:416; JSONP:JAVADOC:427;
+   * 
+   * @test_Strategy: Tests the JsonGeneratorFactory API.
+   *
+   * JsonGeneratorFactory generatorFactory =
+   * Json.createGeneratorFactory(Map<String, ?>); Map<String, ?> config =
+   * JsonGeneratorFactory.getConfigInUse();
+   *
+   * Test for the following 3 scenarios: 1) no supported provider property
+   * (empty config) 2) supported provider property 3) supported and non
+   * supported provider property
+   */
+  @Test
+  public void jsonGeneratorFactoryTest4() throws Fault {
+    boolean pass = true;
+    JsonGeneratorFactory generatorFactory;
+    Map<String, ?> config;
+    try {
+      System.out.println("----------------------------------------------");
+      System.out.println("Test scenario1: no supported provider property");
+      System.out.println("----------------------------------------------");
+      System.out.println(
+          "Create JsonGeneratorFactory with Map<String, ?> with EMPTY config");
+      generatorFactory = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig());
+      config = generatorFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-------------------------------------------");
+      System.out.println("Test scenario2: supported provider property");
+      System.out.println("-------------------------------------------");
+      System.out.println(
+          "Create JsonGeneratorFactory with Map<String, ?> with PRETTY_PRINTING config");
+      generatorFactory = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig());
+      config = generatorFactory.getConfigInUse();
+      String[] props = { JsonGenerator.PRETTY_PRINTING, };
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+
+      System.out.println("-------------------------------------------------------------");
+      System.out.println("Test scenario3: supported and non supported provider property");
+      System.out.println("-------------------------------------------------------------");
+      System.out.println("Create JsonGeneratorFactory with Map<String, ?> with all config");
+      generatorFactory = Json.createGeneratorFactory(JSONP_Util.getAllConfig());
+      config = generatorFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorFactoryTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorFactoryTest4 Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/ClientTests.java
new file mode 100644
index 0000000..616dece
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/ClientTests.java
@@ -0,0 +1,2142 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsongeneratortests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* private Utility methods */
+
+  /*********************************************************************************
+   * generateSimpleJsonObject
+   *********************************************************************************/
+  private void generateSimpleJsonObject(JsonGenerator generator) {
+    try {
+      generator.writeStartObject().writeStartObject("object")
+          .write("string", "string").write("number", 1)
+          .write("true", JsonValue.TRUE).write("false", JsonValue.FALSE)
+          .write("null", JsonValue.NULL).writeEnd().writeStartArray("array")
+          .write("string").write(1).write(JsonValue.TRUE).write(JsonValue.FALSE)
+          .write(JsonValue.NULL).writeEnd().writeEnd();
+      generator.close();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+    }
+  }
+
+  /*********************************************************************************
+   * generateSimpleJsonArray
+   *********************************************************************************/
+  private void generateSimpleJsonArray(JsonGenerator generator) {
+    try {
+      generator.writeStartArray().writeStartObject().write("string", "string")
+          .write("number", 1).write("true", JsonValue.TRUE)
+          .write("false", JsonValue.FALSE).write("null", JsonValue.NULL)
+          .writeEnd().writeStartArray().write("string").write(1)
+          .write(JsonValue.TRUE).write(JsonValue.FALSE).write(JsonValue.NULL)
+          .writeEnd().writeEnd();
+      generator.close();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+    }
+  }
+
+  /*********************************************************************************
+   * generateJsonObject
+   *********************************************************************************/
+  private String generateJsonObject() {
+    try {
+      System.out.println("Generate a JsonObject");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("emptyString", "")
+          .writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("intMin", Integer.MIN_VALUE).write("intMax", Integer.MAX_VALUE)
+          .write("longMin", Long.MIN_VALUE).write("longMax", Long.MAX_VALUE)
+          .write("doubleMin", Double.MIN_VALUE)
+          .write("doubleMax", Double.MAX_VALUE)
+          .write("bigInteger",
+              new BigInteger(Integer.toString(Integer.MAX_VALUE)))
+          .write("bigDecimal", BigDecimal.valueOf(Integer.MIN_VALUE))
+          .write("true", JsonValue.TRUE).write("false", JsonValue.FALSE)
+          .write("null", JsonValue.NULL).writeStartObject("object")
+          .write("emptyString", "").writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("number", 100).write("true", true).write("false", false)
+          .writeNull("null").writeStartObject("object").write("name", "value")
+          .write("objectFooBar", JSONP_Util.buildJsonObjectFooBar())
+          .write("arrayFooBar", JSONP_Util.buildJsonArrayFooBar()).writeEnd()
+          .writeStartArray("array").write("one").write("two")
+          .write(JSONP_Util.buildJsonObjectFooBar())
+          .write(JSONP_Util.buildJsonArrayFooBar()).writeEnd().writeEnd()
+          .writeStartArray("array").write("string").write(Integer.MAX_VALUE)
+          .write(Long.MAX_VALUE).write(Double.MAX_VALUE)
+          .write(new BigInteger(Integer.toString(Integer.MAX_VALUE)))
+          .write(JsonValue.TRUE).write(JsonValue.FALSE).write(JsonValue.NULL)
+          .writeStartObject().write("name", "value").writeEnd()
+          .writeStartArray().write("one").write("two").writeEnd().writeEnd()
+          .write("asciiChars",
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .writeEnd();
+      generator.close();
+      return sWriter.toString();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * buildJsonObject
+   *********************************************************************************/
+  private JsonObject buildJsonObject() {
+    try {
+      System.out.println("Build a JsonObject");
+      JsonObject jsonObject = Json.createObjectBuilder().add("emptyString", "")
+          .add("emptyArray", Json.createArrayBuilder())
+          .add("emptyObject", Json.createObjectBuilder())
+          .add("string", "string").add("intMin", Integer.MIN_VALUE)
+          .add("intMax", Integer.MAX_VALUE).add("longMin", Long.MIN_VALUE)
+          .add("longMax", Long.MAX_VALUE).add("doubleMin", Double.MIN_VALUE)
+          .add("doubleMax", Double.MAX_VALUE)
+          .add("bigInteger",
+              new BigInteger(Integer.toString(Integer.MAX_VALUE)))
+          .add("bigDecimal", BigDecimal.valueOf(Integer.MIN_VALUE))
+          .add("true", JsonValue.TRUE).add("false", JsonValue.FALSE)
+          .add("null", JsonValue.NULL)
+          .add("object",
+              Json.createObjectBuilder().add("emptyString", "")
+                  .add("emptyArray", Json.createArrayBuilder())
+                  .add("emptyObject", Json.createObjectBuilder())
+                  .add("string", "string").add("number", 100)
+                  .add("true", JsonValue.TRUE).add("false", JsonValue.FALSE)
+                  .add("null", JsonValue.NULL)
+                  .add("object", Json.createObjectBuilder().add("name", "value")
+                      .add("objectFooBar", JSONP_Util.buildJsonObjectFooBar())
+                      .add("arrayFooBar", JSONP_Util.buildJsonArrayFooBar()))
+                  .add("array",
+                      Json.createArrayBuilder().add("one").add("two")
+                          .add(JSONP_Util.buildJsonObjectFooBar())
+                          .add(JSONP_Util.buildJsonArrayFooBar())))
+          .add("array",
+              Json.createArrayBuilder().add("string").add(Integer.MAX_VALUE)
+                  .add(Long.MAX_VALUE).add(Double.MAX_VALUE)
+                  .add(new BigInteger(Integer.toString(Integer.MAX_VALUE)))
+                  .add(JsonValue.TRUE).add(JsonValue.FALSE).add(JsonValue.NULL)
+                  .add(Json.createObjectBuilder().add("name", "value"))
+                  .add(Json.createArrayBuilder().add("one").add("two")))
+          .add("asciiChars",
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .build();
+      return jsonObject;
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * generateJsonArray
+   *********************************************************************************/
+  private String generateJsonArray() {
+    try {
+      System.out.println("Generate a JsonArray");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("").writeStartArray().writeEnd()
+          .writeStartObject().writeEnd().write("string")
+          .write(Integer.MIN_VALUE).write(Integer.MAX_VALUE)
+          .write(Long.MIN_VALUE).write(Long.MAX_VALUE).write(Double.MIN_VALUE)
+          .write(Double.MAX_VALUE)
+          .write(new BigInteger(new Integer(Integer.MAX_VALUE).toString()))
+          .write(BigDecimal.valueOf(Integer.MIN_VALUE)).write(JsonValue.TRUE)
+          .write(JsonValue.FALSE).write(JsonValue.NULL).writeStartObject()
+          .write("emptyString", "").writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("number", 100).write("true", JsonValue.TRUE)
+          .write("false", JsonValue.FALSE).write("null", JsonValue.NULL)
+          .writeStartObject("object").write("name", "value")
+          .write("objectFooBar", JSONP_Util.buildJsonObjectFooBar())
+          .write("arrayFooBar", JSONP_Util.buildJsonArrayFooBar()).writeEnd()
+          .writeStartArray("array").write("one").write("two")
+          .write(JSONP_Util.buildJsonObjectFooBar())
+          .write(JSONP_Util.buildJsonArrayFooBar()).writeEnd().writeEnd()
+          .writeStartArray().write("string").write(Integer.MAX_VALUE)
+          .write(Long.MAX_VALUE).write(Double.MAX_VALUE)
+          .write(new BigInteger(new Integer(Integer.MAX_VALUE).toString()))
+          .write(true).write(false).writeNull().writeStartObject()
+          .write("name", "value").writeEnd().writeStartArray().write("one")
+          .write("two").writeEnd().writeEnd()
+          .write(
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .writeEnd();
+      generator.close();
+      return sWriter.toString();
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  /*********************************************************************************
+   * buildJsonArray
+   *********************************************************************************/
+  private JsonArray buildJsonArray() {
+    try {
+      System.out.println("Build a JsonArray");
+      JsonArray jsonArray = Json.createArrayBuilder().add("")
+          .add(Json.createArrayBuilder()).add(Json.createObjectBuilder())
+          .add("string").add(Integer.MIN_VALUE).add(Integer.MAX_VALUE)
+          .add(Long.MIN_VALUE).add(Long.MAX_VALUE).add(Double.MIN_VALUE)
+          .add(Double.MAX_VALUE)
+          .add(new BigInteger(new Integer(Integer.MAX_VALUE).toString()))
+          .add(BigDecimal.valueOf(Integer.MIN_VALUE)).add(JsonValue.TRUE)
+          .add(JsonValue.FALSE).add(JsonValue.NULL)
+          .add(Json.createObjectBuilder().add("emptyString", "")
+              .add("emptyArray", Json.createArrayBuilder())
+              .add("emptyObject", Json.createObjectBuilder())
+              .add("string", "string").add("number", 100)
+              .add("true", JsonValue.TRUE).add("false", JsonValue.FALSE)
+              .add("null", JsonValue.NULL)
+              .add("object",
+                  Json.createObjectBuilder().add("name", "value")
+                      .add("objectFooBar", JSONP_Util.buildJsonObjectFooBar())
+                      .add("arrayFooBar", JSONP_Util.buildJsonArrayFooBar()))
+              .add("array",
+                  Json.createArrayBuilder().add("one").add("two")
+                      .add(JSONP_Util.buildJsonObjectFooBar())
+                      .add(JSONP_Util.buildJsonArrayFooBar())))
+          .add(Json.createArrayBuilder().add("string").add(Integer.MAX_VALUE)
+              .add(Long.MAX_VALUE).add(Double.MAX_VALUE)
+              .add(new BigInteger(new Integer(Integer.MAX_VALUE).toString()))
+              .add(JsonValue.TRUE).add(JsonValue.FALSE).add(JsonValue.NULL)
+              .add(Json.createObjectBuilder().add("name", "value"))
+              .add(Json.createArrayBuilder().add("one").add("two")))
+          .add(
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .build();
+      return jsonArray;
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      return null;
+    }
+  }
+
+  /* Tests */
+
+  /*
+   * @testName: jsonGeneratorObjectTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:131;
+   * JSONP:JAVADOC:289;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * The output is written to a writer, read back as a string and filtered to
+   * remove whitespace and is compared against an expected string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] }
+   */
+  @Test
+  public void jsonGeneratorObjectTest1() throws Fault {
+    boolean pass = true;
+    try {
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generateSimpleJsonObject(generator);
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(sWriter.toString());
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:168;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:307; JSONP:JAVADOC:327; JSONP:JAVADOC:339;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * The output is written to a writer, read back as a string and filtered to
+   * remove whitespace and is compared against an expected string.
+   *
+   * { "emptyString":"", "emptyArray":[], "emptyObject":{}, "string":"string","+
+   * "intMin":Integer.MIN_VALUE, "intMax":Integer.MAX_VALUE,
+   * "longMin":Long.MIN_VALUE, "longMax":Long.MAX_VALUE, "true":true,
+   * "false":false, "null":null, "object": { "emptyString":"", "emptyArray":[],
+   * "emptyObject":{}, "string":"string", "number":100, "true":true,
+   * "false":false, "null":null, "object":{"name":"value"} "array":["one","two"]
+   * }, "array": [ "string", Integer.MAX_VALUE, Long.MAX_VALUE, true, false,
+   * null, {"name":"value"}, ["one","two"] ],
+   * "asciiChars":"\"\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+   * }
+   */
+  @Test
+  public void jsonGeneratorObjectTest2() throws Fault {
+    boolean pass = true;
+    try {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json.createGenerator(baos);
+      generator.writeStartObject().write("emptyString", "")
+          .writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("intMin", Integer.MIN_VALUE).write("intMax", Integer.MAX_VALUE)
+          .write("longMin", Long.MIN_VALUE).write("longMax", Long.MAX_VALUE)
+          .write("true", JsonValue.TRUE).write("false", JsonValue.FALSE)
+          .write("null", JsonValue.NULL).writeStartObject("object")
+          .write("emptyString", "").writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("number", 100).write("true", JsonValue.TRUE)
+          .write("false", JsonValue.FALSE).write("null", JsonValue.NULL)
+          .writeStartObject("object").write("name", "value").writeEnd()
+          .writeStartArray("array").write("one").write("two").writeEnd()
+          .writeEnd().writeStartArray("array").write("string")
+          .write(Integer.MAX_VALUE).write(Long.MAX_VALUE).write(JsonValue.TRUE)
+          .write(JsonValue.FALSE).write(JsonValue.NULL).writeStartObject()
+          .write("name", "value").writeEnd().writeStartArray().write("one")
+          .write("two").writeEnd().writeEnd()
+          .write("asciiChars",
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .writeEnd();
+      generator.close();
+
+      System.out.println("Dump of string: " + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"emptyString\":\"\",\"emptyArray\":[],\"emptyObject\":{},\"string\":\"string\","
+          + "\"intMin\":" + Integer.MIN_VALUE + "," + "\"intMax\":"
+          + Integer.MAX_VALUE + "," + "\"longMin\":" + Long.MIN_VALUE + ","
+          + "\"longMax\":" + Long.MAX_VALUE + ","
+          + "\"true\":true,\"false\":false,\"null\":null,\"object\":{\"emptyString\":\"\",\"emptyArray\":[],"
+          + "\"emptyObject\":{},\"string\":\"string\",\"number\":100,\"true\":true,\"false\":false,"
+          + "\"null\":null,\"object\":{\"name\":\"value\"},"
+          + "\"array\":[\"one\",\"two\"]},\"array\":[\"string\","
+          + Integer.MAX_VALUE + "," + Long.MAX_VALUE + ",true,false,null,"
+          + "{\"name\":\"value\"},[\"one\",\"two\"]],\"asciiChars\":"
+          + "\"\\\\\\\"\\\\\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM\""
+          + "}";
+
+      System.out.println("Read the JSON text back from OutputStream removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:131;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:307; JSONP:JAVADOC:327; JSONP:JAVADOC:339;
+   * JSONP:JAVADOC:301; JSONP:JAVADOC:310; JSONP:JAVADOC:329; JSONP:JAVADOC:323;
+   * JSONP:JAVADOC:298; JSONP:JAVADOC:314; JSONP:JAVADOC:334;
+   * 
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * The output is written to a writer, read back as a JsonObject and compared
+   * against an expected JsonObject.
+   *
+   * { "emptyString":"", "emptyArray":[], "emptyObject":{}, "string":"string","+
+   * "intMin":Integer.MIN_VALUE, "intMax":Integer.MAX_VALUE,
+   * "longMin":Long.MIN_VALUE, "longMax":Long.MAX_VALUE,
+   * "doubleMin":Double.MIN_VALUE, "doubleMax":Double.MAX_VALUE,
+   * "bigInteger":Integer.MAX_VALUE, "bigDecimal":Integer.MIN_VALUE,
+   * "true":true, "false":false, "null":null, "object": { "emptyString":"",
+   * "emptyArray":[], "emptyObject":{}, "string":"string", "number":100,
+   * "true":true, "false":false, "null":null,
+   * "object":{"name":"value",{"foo":"bar"}},
+   * "array":["one","two",["foo","bar"]] }, "array": [ "string",
+   * Integer.MAX_VALUE, Long.MAX_VALUE, Double.MAX_VALUE, Integer.MAX_VALUE
+   * true, false, null, {"name":"value"}, ["one","two"] ],
+   * "asciiChars":"\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+   * }
+   */
+  @Test
+  public void jsonGeneratorObjectTest3() throws Fault {
+    boolean pass = true;
+    try {
+      JsonObject expJsonObject = buildJsonObject();
+      String jsonText = generateJsonObject();
+
+      JsonReader reader = Json.createReader(new StringReader(jsonText));
+      JsonObject actJsonObject = (JsonObject) reader.read();
+
+      // Do comparison
+      System.out.println("Compare expJsonObject and actJsonObject for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(expJsonObject, actJsonObject);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:416;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:163;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * Encoding is done in UTF-16BE. The output is written to an OutputStream as
+   * UTF-16BE encoding, read back as a string using UTF-16BE encoding and
+   * filtered to remove whitespace and is compared against an expected string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] } Tests the following API
+   * call:
+   *
+   * JsonGenerator generator =
+   * Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream,
+   * Charset);
+   */
+  @Test
+  public void jsonGeneratorObjectTest4() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create generator output in UTF-16BE encoding.");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16BE);
+      generateSimpleJsonObject(generator);
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println(
+          "Read the JSON text back encoding from OutputStream using UTF-16BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16BE"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:131; JSONP:JAVADOC:341; JSONP:JAVADOC:295;
+   * JSONP:JAVADOC:289;
+   * 
+   * @test_Strategy: Test generation of object data with multiple unicode chars
+   * in data. The output is written to a writer, read back using a reader as a
+   * JsonObject and extracts the JsonString value out and compares it against
+   * the expected JsonString value.
+   *
+   * Generate the following object of unicode char(s):
+   *
+   * {"unicodechars":"\u0000\u000f\u001f\u00ff\uff00\uffff"}
+   */
+  @Test
+  public void jsonGeneratorObjectTest5() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String expUnicodeChars = "\u0000\u000f\u001f\u00ff\uff00\uffff";
+    try {
+
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject()
+          .write("unicodechars", "\u0000\u000f\u001f\u00ff\uff00\uffff")
+          .writeEnd();
+      generator.close();
+      sWriter.close();
+
+      System.out.println("Testing read of " + sWriter.toString());
+      reader = Json.createReader(new StringReader(sWriter.toString()));
+      JsonObject jsonObject = reader.readObject();
+      String actUnicodeChars = jsonObject.getJsonString("unicodechars")
+          .getString();
+      reader.close();
+      System.out.println("actUnicodeChars=" + actUnicodeChars);
+
+      pass = JSONP_Util.assertEquals(expUnicodeChars, actUnicodeChars);
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      pass = false;
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorArrayTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:339; JSONP:JAVADOC:341; JSONP:JAVADOC:295;
+   * JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317; JSONP:JAVADOC:325;
+   * JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:131; JSONP:JAVADOC:289;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonArray.
+   * The output is written to a writer, read back as a string and filtered to
+   * remove whitespace and is compared against an expected string.
+   *
+   * [ {"string":"string","number":1,"true":true,"false":false,"null":null},
+   * ["string", 1, true, false, null] ]
+   *
+   */
+  @Test
+  public void jsonGeneratorArrayTest1() throws Fault {
+    boolean pass = true;
+    try {
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generateSimpleJsonArray(generator);
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "[{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},[\"string\",1,true,false,null]]";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(sWriter.toString());
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorArrayTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorArrayTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorArrayTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:168;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:327; JSONP:JAVADOC:339;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonArray.
+   * The output is written to a writer, read back as a string and filtered to
+   * remove whitespace and is compared against an expected string.
+   *
+   * [ "", [], {}, "string", Integer.MIN_VALUE, Integer.MAX_VALUE,
+   * Long.MIN_VALUE, Long.MAX_VALUE, true, false, null, { "emptyString":"",
+   * "emptyArray":[], "emptyObject":{}, "string":"string", "number":100,
+   * "true":true, "false":false, "null":null, "object":{"name":"value"},
+   * "array":["one","two"] }, [ "string", Integer.MAX_VALUE, Long.MAX_VALUE,
+   * true, false, null, {"name":"value"}, ["one","two"] ],
+   * "\"\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+   * ]
+   */
+  @Test
+  public void jsonGeneratorArrayTest2() throws Fault {
+    boolean pass = true;
+    try {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json.createGenerator(baos);
+      generator.writeStartArray().write("").writeStartArray().writeEnd()
+          .writeStartObject().writeEnd().write("string")
+          .write(Integer.MIN_VALUE).write(Integer.MAX_VALUE)
+          .write(Long.MIN_VALUE).write(Long.MAX_VALUE).write(JsonValue.TRUE)
+          .write(JsonValue.FALSE).write(JsonValue.NULL).writeStartObject()
+          .write("emptyString", "").writeStartArray("emptyArray").writeEnd()
+          .writeStartObject("emptyObject").writeEnd().write("string", "string")
+          .write("number", 100).write("true", JsonValue.TRUE)
+          .write("false", JsonValue.FALSE).write("null", JsonValue.NULL)
+          .writeStartObject("object").write("name", "value").writeEnd()
+          .writeStartArray("array").write("one").write("two").writeEnd()
+          .writeEnd().writeStartArray().write("string").write(Integer.MAX_VALUE)
+          .write(Long.MAX_VALUE).write(JsonValue.TRUE).write(JsonValue.FALSE)
+          .write(JsonValue.NULL).writeStartObject().write("name", "value")
+          .writeEnd().writeStartArray().write("one").write("two").writeEnd()
+          .writeEnd()
+          .write(
+              "\\\"\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")
+          .writeEnd();
+      generator.close();
+
+      System.out.println("Dump of string: " + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "[\"\",[],{},\"string\"," + Integer.MIN_VALUE + ","
+          + Integer.MAX_VALUE + "," + Long.MIN_VALUE + "," + Long.MAX_VALUE
+          + "," + "true,false,null,{\"emptyString\":\"\",\"emptyArray\":[],"
+          + "\"emptyObject\":{},\"string\":\"string\",\"number\":100,\"true\":true,\"false\":false,"
+          + "\"null\":null,\"object\":{\"name\":\"value\"},"
+          + "\"array\":[\"one\",\"two\"]},[\"string\"," + Integer.MAX_VALUE
+          + "," + Long.MAX_VALUE + ",true,false,null,"
+          + "{\"name\":\"value\"},[\"one\",\"two\"]],"
+          + "\"\\\\\\\"\\\\\\\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM\""
+          + "]";
+
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorArrayTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorArrayTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorArrayTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:131;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:327; JSONP:JAVADOC:339; JSONP:JAVADOC:329;
+   * JSONP:JAVADOC:321; JSONP:JAVADOC:323; JSONP:JAVADOC:332; JSONP:JAVADOC:337;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonArray.
+   * The output is written to a writer, read back as a JsonArray and compared
+   * against an expected JsonArray.
+   *
+   * [ "", [], {}, "string", Integer.MIN_VALUE, Integer.MAX_VALUE,
+   * Long.MIN_VALUE, Long.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE,
+   * Integer.MAX_VALUE, Integer.MIN_VALUE, true, false, null, {
+   * "emptyString":"", "emptyArray":[], "emptyObject":{}, "string":"string",
+   * "number":100, "true":true, "false":false, "null":null,
+   * "object":{"name":"value",{"foo":"bar"}},
+   * "array":["one","two",["foo","bar"]] }, [ "string", Integer.MAX_VALUE,
+   * Long.MAX_VALUE, Double.MAX_VALUE, Integer.MAX_VALUE true, false, null,
+   * {"name":"value"}, ["one","two"] ],
+   * "\"\\!@#$%^&*()_+|~1234567890-=`[]{}:;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+   * ]
+   */
+  @Test
+  public void jsonGeneratorArrayTest3() throws Fault {
+    boolean pass = true;
+    try {
+      JsonArray expJsonArray = buildJsonArray();
+      String jsonText = generateJsonArray();
+      System.out.println("generator json text: " + jsonText);
+
+      JsonReader reader = Json.createReader(new StringReader(jsonText));
+      JsonArray actJsonArray = (JsonArray) reader.read();
+
+      // Do comparison
+      System.out.println("Compare expJsonArray and actJsonArray for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorArrayTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorArrayTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorArrayTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:339; JSONP:JAVADOC:341; JSONP:JAVADOC:115;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:163; JSONP:JAVADOC:289;
+   * JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonArray.
+   * Encoding is done in UTF-16BE. The output is written to an OutputStream as
+   * UTF-16BE encoding, read back as a string using UTF-16BE encoding and
+   * filtered to remove whitespace and is compared against an expected string.
+   *
+   * [ {"string":"string","number":1,"true":true,"false":false,"null":null},
+   * ["string", 1, true, false, null] ]
+   *
+   */
+  @Test
+  public void jsonGeneratorArrayTest4() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create generator output in UTF-16BE encoding.");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16BE);
+      generateSimpleJsonArray(generator);
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "[{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},[\"string\",1,true,false,null]]";
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16BE"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorArrayTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorArrayTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorArrayTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:131; JSONP:JAVADOC:339; JSONP:JAVADOC:319;
+   * JSONP:JAVADOC:289;
+   * 
+   * @test_Strategy: Test generation of array data with multiple unicode chars
+   * in data. The output is written to a writer, read back using a reader as a
+   * JsonArray and extracts the JsonString value out and compares it against the
+   * expected JsonString value.
+   *
+   * Generate the following array of unicode char(s):
+   *
+   * ["\u0000\u000f\u001f\u00ff\uff00\uffff"]
+   */
+  @Test
+  public void jsonGeneratorArrayTest5() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String expUnicodeChars = "\u0000\u000f\u001f\u00ff\uff00\uffff";
+    try {
+
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("\u0000\u000f\u001f\u00ff\uff00\uffff")
+          .writeEnd();
+      generator.close();
+      sWriter.close();
+
+      System.out.println("Testing read of " + sWriter.toString());
+      reader = Json.createReader(new StringReader(sWriter.toString()));
+      JsonArray jsonArray = reader.readArray();
+      String actUnicodeChars = jsonArray.getJsonString(0).getString();
+      reader.close();
+      System.out.println("actUnicodeChars=" + actUnicodeChars);
+
+      pass = JSONP_Util.assertEquals(expUnicodeChars, actUnicodeChars);
+    } catch (Exception e) {
+      System.err.println("Exception occurred: " + e);
+      pass = false;
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorArrayTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectConfigTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:289;
+   * JSONP:JAVADOC:162; JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * This test uses the configuration feature to PRETTY PRINT. The output is
+   * written to a Writer, read back as a string and filtered to remove
+   * whitespace and is compared against an expected string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] } Tests following API call:
+   *
+   * JsonGenerator generator = Json.createGeneratorFactory(Map<String,
+   * ?>).createGenerator(Writer)
+   */
+  @Test
+  public void jsonGeneratorObjectConfigTest1() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create JsonGenerator using configuration with PRETTY_PRINTING");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig())
+          .createGenerator(sWriter);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output with PRETTY_PRINTING feature
+      System.out.println("PRETTY_PRINTING feature\n" + sWriter.toString());
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(sWriter.toString());
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectConfigTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectConfigTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectConfigTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:289;
+   * JSONP:JAVADOC:200; JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * This test uses the configuration feature to PRETTY PRINT. The output is
+   * written to a OutputStream, read back as a string and filtered to remove
+   * whitespace and is compared against an expected string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] } Tests following API call:
+   *
+   * JsonGenerator generator = Json.createGeneratorFactory(Map<String,
+   * ?>).createGenerator(OutputStream)
+   */
+  @Test
+  public void jsonGeneratorObjectConfigTest2() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create JsonGenerator using configuration with PRETTY_PRINTING");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig())
+          .createGenerator(baos);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output with PRETTY_PRINTING feature
+      System.out.println("PRETTY_PRINTING feature\n" + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectConfigTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectConfigTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectEncodingTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:163;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * The output is written to an OutputStream using UTF-8 encoding, read back as
+   * a string and filtered to remove whitespace and is compared against an
+   * expected string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] }
+   */
+  @Test
+  public void jsonGeneratorObjectEncodingTest1() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create JsonGenerator using UTF-8 encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_8);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generator Output=" + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-8 encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectEncodingTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectEncodingTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorObjectEncodingTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:289;
+   * JSONP:JAVADOC:163; JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   * This test uses the configuration feature to PRETTY PRINT. The output is
+   * written to an OutputStream using UTF-16BE encoding, read back as a string
+   * and filtered to remove whitespace and is compared against an expected
+   * string.
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] } Tests the following API
+   * call:
+   *
+   * JsonGenerator generator = Json.createGeneratorFactory(Map<String,
+   * ?>).createGenerator(OutputStream, Charset);
+   */
+  @Test
+  public void jsonGeneratorObjectEncodingTest2() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println(
+          "Create JsonGenerator using configuration with PRETTY_PRINTING using UTF-16BE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getPrettyPrintingConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16BE);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output with PRETTY_PRINTING feature
+      System.out.println("PRETTY_PRINTING feature\n" + baos.toString("UTF-16BE"));
+
+      // Do comparison
+      System.out.println("Create expected JSON text with no whitespace");
+      String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16BE"));
+      pass = JSONP_Util.assertEqualsJsonText(expJson, actJson);
+
+    } catch (Exception e) {
+      throw new Fault("jsonGeneratorObjectEncodingTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorObjectEncodingTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorUTFEncodedTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * JSONP:JAVADOC:295; JSONP:JAVADOC:304; JSONP:JAVADOC:292; JSONP:JAVADOC:317;
+   * JSONP:JAVADOC:325; JSONP:JAVADOC:319; JSONP:JAVADOC:115; JSONP:JAVADOC:163;
+   * JSONP:JAVADOC:289; JSONP:JAVADOC:416;
+   * 
+   * @test_Strategy: Tests various JsonGenerator API's to create a JsonObject.
+   *
+   * The output is written to an OutputStream using all supported UTF encodings
+   * and read back as a string and filtered to remove whitespace and is compared
+   * against an expected string. The following UTF encodings are tested:
+   *
+   * UTF8 UTF16 UTF16LE UTF16BE UTF32LE UTF32BE
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] }
+   */
+  @Test
+  public void jsonGeneratorUTFEncodedTests() throws Fault {
+    boolean pass = true;
+    System.out.println(
+        "Create expected JSON text with no whitespace for use with comparison");
+    String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+    try {
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-8]");
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-8 encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_8);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-8 encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing generation to UTF-8 encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "------------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-16]");
+      System.out.println(
+          "------------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-16 encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16 encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing generation to UTF-16 encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-16LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-16LE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16LE);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16LE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16LE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16LE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-16LE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-16BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-16BE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_16BE);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16BE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16BE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-16BE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-32LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-32LE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_32LE);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-32LE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-32LE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-32LE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-32LE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createGeneratorFactory(Map<String,?>).createGenerator(OutputStream, Charset) as UTF-32BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonGenerator using UTF-32BE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json
+          .createGeneratorFactory(JSONP_Util.getEmptyConfig())
+          .createGenerator(baos, JSONP_Util.UTF_32BE);
+      generateSimpleJsonObject(generator);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-32BE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-32BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-32BE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-32BE encoding: " + e);
+    }
+    if (!pass)
+      throw new Fault("jsonGeneratorUTFEncodedTests Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:339; JSONP:JAVADOC:341; JSONP:JAVADOC:115;
+   * JSONP:JAVADOC:293; JSONP:JAVADOC:296; JSONP:JAVADOC:299; JSONP:JAVADOC:302;
+   * JSONP:JAVADOC:305; JSONP:JAVADOC:308; JSONP:JAVADOC:311; JSONP:JAVADOC:315;
+   * JSONP:JAVADOC:297; JSONP:JAVADOC:303; JSONP:JAVADOC:306; JSONP:JAVADOC:309;
+   * JSONP:JAVADOC:312; JSONP:JAVADOC:316; JSONP:JAVADOC:336; JSONP:JAVADOC:335;
+   * JSONP:JAVADOC:290; JSONP:JAVADOC:331; JSONP:JAVADOC:381; JSONP:JAVADOC:382;
+   * JSONP:JAVADOC:350; JSONP:JAVADOC:352; JSONP:JAVADOC:354; JSONP:JAVADOC:356;
+   * JSONP:JAVADOC:358; JSONP:JAVADOC:360; JSONP:JAVADOC:362; JSONP:JAVADOC:364;
+   * JSONP:JAVADOC:366; JSONP:JAVADOC:347; JSONP:JAVADOC:348; JSONP:JAVADOC:368;
+   * JSONP:JAVADOC:370; JSONP:JAVADOC:372; JSONP:JAVADOC:374;
+   * 
+   * @test_Strategy: Tests various exception test cases.
+   *
+   * NumberFormatException JsonGenerationException
+   *
+   */
+  @Test
+  public void jsonGeneratorExceptionTests() throws Fault {
+    boolean pass = true;
+
+    // Test NumberFormatException for write(double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write(Double.NaN);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test NumberFormatException for write(double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write(Double.NEGATIVE_INFINITY);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test NumberFormatException for write(double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write(Double.POSITIVE_INFINITY);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test NumberFormatException for write(String,double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(String,double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("badnumber", Double.NaN);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test NumberFormatException for write(String,double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(String,double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("badnumber", Double.NEGATIVE_INFINITY);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test NumberFormatException for write(String,double) if value is
+    // Not-a-Number(NaN) or infinity
+    try {
+      System.out.println(
+          "Trip NumberFormatException for write(String,double) if value is Not-a-Number(NaN) or infinity");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("badnumber", Double.POSITIVE_INFINITY);
+      System.err.println("Did not get expected NumberFormatException");
+      pass = false;
+    } catch (NumberFormatException e) {
+      System.out.println("Caught expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonGenerationExceptipn if an incomplete JSON is generated.
+    try {
+      System.out.println(
+          "Trip JsonGenerationExceptipn if an incomplete JSON is generated.");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("name", "value");
+      generator.close();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonGenerationExceptipn if an incomplete JSON is generated.
+    try {
+      System.out.println(
+          "Trip JsonGenerationExceptipn if an incomplete JSON is generated.");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string");
+      generator.close();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(JsonValue) if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(JsonValue) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(JsonValue.TRUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String) if not called within array
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("name");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(BigInteger) if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(BigInteger) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject()
+          .write(new BigInteger(new Integer(Integer.MAX_VALUE).toString()));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(BigDecimal) if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(BigDecimal) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(BigDecimal.valueOf(Integer.MIN_VALUE));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(int) if not called within array
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(int) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(Integer.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(long) if not called within array
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(long) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(Long.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(double) if not called within array
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(double) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(Double.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(boolean) if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(boolean) if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write(true);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeNull() if not called within array
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeNull() if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeNull();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeStartArray() if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeStartArray() if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeStartArray();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeStartObject() if not called within
+    // array context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeStartObject() if not called within array context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeStartObject();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,JsonValue) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,JsonValue) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", JsonValue.TRUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,String) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,String) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", "name");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,BigInteger) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,BigInteger) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string",
+          new BigInteger(new Integer(Integer.MAX_VALUE).toString()));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,BigDecimal) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,BigDecimal) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string",
+          BigDecimal.valueOf(Integer.MIN_VALUE));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,int) if not called within
+    // object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,int) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", Integer.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,long) if not called within
+    // object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,long) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", Long.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,double) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,double) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", Double.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,boolean) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,boolean) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write("string", true);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeNull(String) if not called within
+    // object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeNull(String) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeNull("string");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeStartArray(String) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeStartArray(String) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeStartArray("string");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeStartObject(String) if not called
+    // within object context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeStartObject(String) if not called within object context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeStartObject("string");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,JsonValue) when invoked
+    // after the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,JsonValue) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name", "value");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeEnd() when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeEnd() when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().writeEnd();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,BigInteger) when invoked
+    // after the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,BigInteger) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name",
+          new BigInteger(new Integer(Integer.MAX_VALUE).toString()));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,BigDecimal) when invoked
+    // after the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,BigDecimal) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name",
+          BigDecimal.valueOf(Integer.MIN_VALUE));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,int) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,int) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name", Integer.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,long) when invoked after
+    // the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,long) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name", Long.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,double) when invoked after
+    // the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,double) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name", Double.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String,boolean) when invoked after
+    // the writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String,boolean) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().write("name", false);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for writeNull(String) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for writeNull(String) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeEnd().writeNull("name");
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(JsonValue) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(JsonValue) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(JsonValue.TRUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(String) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(String) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(JsonValue.TRUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(BigDecimal) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(BigDecimal) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd()
+          .write(BigDecimal.valueOf(Integer.MIN_VALUE));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(BigInteger) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(BigInteger) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd()
+          .write(new BigInteger(new Integer(Integer.MAX_VALUE).toString()));
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(int) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(int) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(Integer.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(long) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(long) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(Long.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(double) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(double) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(Double.MAX_VALUE);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for write(boolean) when invoked after the
+    // writeEnd method is called
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for write(boolean) when invoked after the writeEnd method is called");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().write(true);
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test JsonGenerationException for for writeNull() when invoked with no
+    // context
+    try {
+      System.out.println(
+          "Trip JsonGenerationException for for writeNull() when invoked with no context");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().writeEnd().writeNull();
+      System.err.println("Did not get expected JsonGenerationException");
+      pass = false;
+    } catch (JsonGenerationException e) {
+      System.out.println("Caught expected JsonGenerationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonGeneratorExceptionTests Failed");
+  }
+
+  /*
+   * @testName: flushTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:131; JSONP:JAVADOC:289; JSONP:JAVADOC:291;
+   * JSONP:JAVADOC:340; JSONP:JAVADOC:341; JSONP:JAVADOC:342;
+   * 
+   * @test_Strategy: Generate some partial Json, flush output and compare
+   * output. Generate additional Json to complete, flush output again and
+   * compare. Test passes if comparisons are correct.
+   *
+   * {"object":{},"array":[]}
+   */
+  @Test
+  public void flushTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Generate some partial Json and flush output.");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().writeStartObject("object").writeEnd()
+          .flush();
+
+      // Do comparison 1
+      System.out.println("Create expected partial JSON text with no whitespace");
+      String expJson = "{\"object\":{}";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(sWriter.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+      System.out.println("Generate additional Json to complete and flush output.");
+      generator.writeStartArray("array").writeEnd().writeEnd().flush();
+
+      // Do comparison 2
+      expJson = "{\"object\":{},\"array\":[]}";
+      System.out.println("Read the JSON text back from Writer removing whitespace");
+      actJson = JSONP_Util.removeWhitespace(sWriter.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+      generator.close();
+
+    } catch (Exception e) {
+      throw new Fault("flushTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("flushTest Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorIOErrorTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:346; JSONP:JAVADOC:551;
+   * 
+   * @test_Strategy: Tests for JsonException for testable i/o errors.
+   *
+   */
+  @Test
+  public void jsonGeneratorIOErrorTests() throws Fault {
+    boolean pass = true;
+
+    // Trip JsonException if there is an i/o error on JsonGenerator.close()
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonGenerator.close().");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      JsonGenerator generator = Json.createGenerator(mbw);
+      generator.writeStartObject().writeEnd();
+      mbw.setThrowIOException(true);
+      System.out.println("Calling JsonGenerator.close()");
+      generator.close();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on JsonGenerator.flush()
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonGenerator.flush().");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      JsonGenerator generator = Json.createGenerator(mbw);
+      generator.writeStartObject().writeEnd();
+      mbw.setThrowIOException(true);
+      System.out.println("Calling JsonGenerator.flush()");
+      generator.flush();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonGeneratorIOErrorTests Failed");
+  }
+
+  /*
+   * @testName: jsonGeneratorDocumentRootTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:317; JSONP:JAVADOC:319; JSONP:JAVADOC:321;
+   * JSONP:JAVADOC:323; JSONP:JAVADOC:325; JSONP:JAVADOC:327; JSONP:JAVADOC:329;
+   * JSONP:JAVADOC:332; JSONP:JAVADOC:583; JSONP:JAVADOC:584; JSONP:JAVADOC:585;
+   * JSONP:JAVADOC:586; JSONP:JAVADOC:587; JSONP:JAVADOC:588; JSONP:JAVADOC:662;
+   * JSONP:JAVADOC:663; JSONP:JAVADOC:664; JSONP:JAVADOC:665; JSONP:JAVADOC:666;
+   * JSONP:JAVADOC:667; JSONP:JAVADOC:289; JSONP:JAVADOC:341; JSONP:JAVADOC:672;
+   * 
+   * @test_Strategy: Tests RFC 7159 grammar changes:<br> {@code
+   * "JSON-text = ws value ws"}<br> {@code
+   * "value     = false / null / true / object / array / number / string"}
+   */
+  @Test
+  public void jsonGeneratorDocumentRootTest() throws Fault {
+    Generator genTest = new Generator();
+    final TestResult result = genTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/Generator.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/Generator.java
new file mode 100644
index 0000000..0064716
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsongeneratortests/Generator.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsongeneratortests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import jakarta.json.Json;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerator;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+import jakarta.json.JsonObject;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests: {@link JsonGenerator}
+ * API methods.
+ */
+public class Generator {
+
+  /** Tests input data. */
+  private static final Object[] VALUES = new Object[] { OBJ_VALUE, // write(JsonValue)
+                                                                   // for
+                                                                   // JsonObject
+      createEmptyArrayWithStr(), // write(JsonValue) for simple JsonArray
+      STR_VALUE, // write(JsonValue) for String
+      INT_VALUE, // write(JsonValue) for int
+      LNG_VALUE, // write(JsonValue) for long
+      DBL_VALUE, // write(JsonValue) for double
+      BIN_VALUE, // write(JsonValue) for BigInteger
+      BDC_VALUE, // write(JsonValue) for BigDecimal
+      BOOL_VALUE, // write(JsonValue) for boolean
+      null // write(JsonValue) for null
+  };
+
+  /**
+   * Test {@link JsonGenerator} API methods.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonGenerator API methods for RFC 7159 grammar changes.");
+    System.out.println("JsonGenerator API methods for RFC 7159 grammar changes.");
+    testPrimitiveTypesInRoot(result);
+    testWrittingObjectByParts(result);
+    return result;
+  }
+
+  /**
+   * Test primitive types as JSON value stored in JSON document root. RFC 7159
+   * grammar changes now allows such a values to be stored in JSON document
+   * root.
+   */
+  private void testPrimitiveTypesInRoot(final TestResult result) {
+    for (Object value : VALUES) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - write(JsonValue) for " + typeName + " as an argument");
+      verifyJsonGeneratorForJsonValue(result, value);
+      verifyJsonGeneratorForSimpleType(result, value);
+    }
+  }
+
+  /**
+   * Verify JSON object generation using low level methods
+   * {@code writeStartObject()}, {@code writeEnd()}, {@code writeKey(String)}
+   * and {@code write(String)}.
+   */
+  private void testWrittingObjectByParts(final TestResult result) {
+    System.out.println(" - generate JSON object");
+    final StringWriter strWriter = new StringWriter();
+    try (JsonGenerator generator = Json.createGenerator(strWriter)) {
+      generator.writeStartObject();
+      generator.writeKey(STR_NAME);
+      generator.write(STR_VALUE);
+      generator.writeEnd();
+    }
+    final String out = strWriter.toString();
+    final JsonObject check = createSimpleObjectStr();
+    if (operationFailed(check, out)) {
+      final String checkStr = check.toString();
+      System.out.println(
+          "     Generated JSON object " + out + " shall be " + checkStr);
+      result.fail("generate JSON object",
+          "Generated value " + out + " shall be " + checkStr);
+    } else {
+      System.out.println("     Output: " + out);
+    }
+
+  }
+
+  /**
+   * Verify JSON document root generation for provided JSON value.
+   */
+  private void verifyJsonGeneratorForJsonValue(final TestResult result,
+      final Object value) {
+    final StringWriter strWriter = new StringWriter();
+    final JsonValue jsonValue = toJsonValue(value);
+    try (JsonGenerator generator = Json.createGenerator(strWriter)) {
+      generator.write(jsonValue);
+    }
+    final String out = strWriter.toString();
+    if (operationFailed(jsonValue, out)) {
+      final String check = jsonValue.toString();
+      System.out.println("     Generated JSON value " + out + " shall be " + check);
+      result.fail("write(JsonValue)",
+          "Generated value " + out + " shall be " + check);
+    } else {
+      System.out.println("     Output (JsonValue): " + out);
+    }
+  }
+
+  /**
+   * Verify JSON document root generation for provided JSON value.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  private void verifyJsonGeneratorForSimpleType(final TestResult result,
+      final Object value) {
+    final StringWriter strWriter = new StringWriter();
+    try (JsonGenerator generator = Json.createGenerator(strWriter)) {
+      switch (JsonValueType.getType(value)) {
+      case String:
+        generator.write((String) value);
+        break;
+      case Integer:
+        generator.write(((Integer) value).intValue());
+        break;
+      case Long:
+        generator.write(((Long) value).longValue());
+        break;
+      case BigInteger:
+        generator.write((BigInteger) value);
+        break;
+      case Double:
+        generator.write(((Double) value).doubleValue());
+        break;
+      case BigDecimal:
+        generator.write((BigDecimal) value);
+        break;
+      case Boolean:
+        generator.write(((Boolean) value).booleanValue() ? JsonValue.TRUE
+            : JsonValue.FALSE);
+        break;
+      case JsonValue:
+        generator.write((JsonValue) value);
+        break;
+      case Null:
+        generator.write(JsonValue.NULL);
+        break;
+      default:
+        throw new IllegalArgumentException(
+            "Value does not match known JSON value type");
+      }
+    }
+    final String out = strWriter.toString();
+    if (operationFailed(value, out)) {
+      final String check = toJsonValue(value).toString();
+      System.out.println("     Generated simple value " + out + " shall be " + check);
+      result.fail("write(JsonValue)",
+          "Generated value " + out + " shall be " + check);
+    } else {
+      System.out.println("     Output (simple): " + out);
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final Object check, final String out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check, final String out) {
+    if (out == null) {
+      return true;
+    }
+    try (final JsonReader reader = Json.createReader(new StringReader(out))) {
+      final JsonValue actVal = reader.readValue();
+      return !assertEquals((JsonValue) check, actVal);
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonnumbertests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonnumbertests/ClientTests.java
new file mode 100644
index 0000000..6c79b66
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonnumbertests/ClientTests.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonnumbertests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonNumberEqualsTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:250;
+   * 
+   * @test_Strategy: Tests JsonNumber equals method. Create 2 equal JsonNumbers
+   * and compare them for equality and expect true. Create 2 non-equal
+   * JsonNumbers and compare them for equality and expect false.
+   */
+  @Test
+  public void jsonNumberEqualsTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonNumber 1 for testing");
+      JsonNumber number1 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number1=" + JSONP_Util.toStringJsonNumber(number1));
+
+      System.out.println("Create sample JsonNumber 2 for testing");
+      JsonNumber number2 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number2=" + JSONP_Util.toStringJsonNumber(number2));
+
+      System.out.println(
+          "Call JsonNumber.equals() to compare 2 equal JsonNumbers and expect true");
+      if (number1.equals(number2)) {
+        System.out.println("JsonNumbers are equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonNumbers are not equal - unexpected.");
+      }
+
+      System.out.println("Create sample JsonNumber 1 for testing");
+      number1 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number1=" + JSONP_Util.toStringJsonNumber(number1));
+
+      System.out.println("Create sample JsonNumber 2 for testing");
+      number2 = JSONP_Util.createJsonNumber((double) 10.25);
+      System.out.println("number2=" + JSONP_Util.toStringJsonNumber(number2));
+
+      System.out.println(
+          "Call JsonNumber.equals() to compare 2 equal JsonNumbers and expect false");
+      if (!number1.equals(number2)) {
+        System.out.println("JsonNumbers are not equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonNumbers are equal - unexpected.");
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonNumberEqualsTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonNumberEqualsTest Failed");
+  }
+
+  /*
+   * @testName: jsonNumberHashCodeTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:251;
+   * 
+   * @test_Strategy: Tests JsonNumber equals method. Create 2 equal JsonNumbers
+   * and compare them for hashcode and expect true. Create 2 non-equal
+   * JsonNumbers and compare them for hashcode and expect false.
+   */
+  @Test
+  public void jsonNumberHashCodeTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonNumber 1 for testing");
+      JsonNumber number1 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number1=" + JSONP_Util.toStringJsonNumber(number1));
+      System.out.println("number1.hashCode()=" + number1.hashCode());
+
+      System.out.println("Create sample JsonNumber 2 for testing");
+      JsonNumber number2 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number2=" + JSONP_Util.toStringJsonNumber(number2));
+      System.out.println("number2.hashCode()=" + number2.hashCode());
+
+      System.out.println(
+          "Call JsonNumber.hashCode() to compare 2 equal JsonNumbers and expect true");
+      if (number1.hashCode() == number2.hashCode()) {
+        System.out.println("JsonNumbers hashCode are equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonNumbers hashCode are not equal - unexpected.");
+      }
+
+      System.out.println("Create sample JsonNumber 1 for testing");
+      number1 = JSONP_Util.createJsonNumber(10);
+      System.out.println("number1=" + JSONP_Util.toStringJsonNumber(number1));
+      System.out.println("number1.hashCode()=" + number1.hashCode());
+
+      System.out.println("Create sample JsonNumber 2 for testing");
+      number2 = JSONP_Util.createJsonNumber((double) 10.25);
+      System.out.println("number2=" + JSONP_Util.toStringJsonNumber(number2));
+      System.out.println("number2.hashCode()=" + number2.hashCode());
+
+      System.out.println(
+          "Call JsonNumber.hashCode() to compare 2 equal JsonNumbers and expect false");
+      if (number1.hashCode() != number2.hashCode()) {
+        System.out.println("JsonNumbers hashCode are not equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonNumbers hashCode are equal - unexpected.");
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonNumberHashCodeTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonNumberHashCodeTest Failed");
+  }
+
+  /*
+   * @testName: jsonNumberIsIntegralTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:51;
+   * 
+   * @test_Strategy: Test JsonNumber.isIntegral() method.
+   */
+  @Test
+  public void jsonNumberIsIntegralTest() throws Fault {
+    boolean pass = true;
+    JsonNumber jsonNumber = null;
+    try {
+      // INTEGRAL NUMBER TEST
+      JsonNumber number1 = JSONP_Util.createJsonNumber(123);
+      if (!JSONP_Util.assertEqualsJsonNumberType(number1.isIntegral(),
+          JSONP_Util.INTEGRAL))
+        pass = false;
+      else {
+        if (!JSONP_Util.assertEquals(123, number1.intValue()))
+          pass = false;
+      }
+      // NON_INTEGRAL NUMBER TEST
+      JsonNumber number2 = JSONP_Util.createJsonNumber(12345.45);
+      if (!JSONP_Util.assertEqualsJsonNumberType(number2.isIntegral(),
+          JSONP_Util.NON_INTEGRAL))
+        pass = false;
+      else {
+        if (!JSONP_Util.assertEquals(12345.45, number2.doubleValue()))
+          pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonNumberIsIntegralTest Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonNumberIsIntegralTest Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ClientTests.java
new file mode 100644
index 0000000..9ba6ce2
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ClientTests.java
@@ -0,0 +1,1360 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonobjecttests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonObjectTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:58; JSONP:JAVADOC:61; JSONP:JAVADOC:64;
+   * JSONP:JAVADOC:67; JSONP:JAVADOC:70; JSONP:JAVADOC:73; JSONP:JAVADOC:76;
+   * JSONP:JAVADOC:80; JSONP:JAVADOC:86; JSONP:JAVADOC:400; JSONP:JAVADOC:401;
+   * JSONP:JAVADOC:402; JSONP:JAVADOC:403; JSONP:JAVADOC:404; JSONP:JAVADOC:406;
+   * JSONP:JAVADOC:408; JSONP:JAVADOC:409;
+   * 
+   * @test_Strategy: Tests JsonObject/JsonObjectBuilder API's. Build a
+   * JsonObject using the JsonObjectBuilder API's then verify that the Map of
+   * JsonObject values matches the expected Map of JsonObject values.
+   */
+  @Test
+  public void jsonObjectTest1() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create the expected map of JsonObject values");
+      Map<String, JsonValue> expMap = new HashMap<>();
+      expMap.put("false", JsonValue.FALSE);
+      expMap.put("true", JsonValue.TRUE);
+      expMap.put("null", JsonValue.NULL);
+      expMap.put("doublemin", JSONP_Util.createJsonNumber(Double.MIN_VALUE));
+      expMap.put("doublemax", JSONP_Util.createJsonNumber(Double.MAX_VALUE));
+      expMap.put("intmin", JSONP_Util.createJsonNumber(Integer.MIN_VALUE));
+      expMap.put("intmax", JSONP_Util.createJsonNumber(Integer.MAX_VALUE));
+      expMap.put("longmin", JSONP_Util.createJsonNumber(Long.MIN_VALUE));
+      expMap.put("longmax", JSONP_Util.createJsonNumber(Long.MAX_VALUE));
+      expMap.put("bigdecimal",
+          JSONP_Util.createJsonNumber(BigDecimal.valueOf(123456789.123456789)));
+      expMap.put("biginteger",
+          JSONP_Util.createJsonNumber(new BigInteger("123456789")));
+      expMap.put("string", JSONP_Util.createJsonString("string1"));
+      expMap.put("false2", JsonValue.FALSE);
+      expMap.put("true2", JsonValue.TRUE);
+      expMap.put("null2", JsonValue.NULL);
+      expMap.put("object", object);
+      expMap.put("array", array);
+      JSONP_Util.dumpMap(expMap, "Expected Map");
+
+      System.out.println("Create JsonObject using all JsonObjectBuilder API's");
+      JsonObject myJsonObject = Json.createObjectBuilder()
+          .add("false", JsonValue.FALSE).add("true", JsonValue.TRUE)
+          .add("null", JsonValue.NULL).add("doublemin", Double.MIN_VALUE)
+          .add("doublemax", Double.MAX_VALUE).add("intmin", Integer.MIN_VALUE)
+          .add("intmax", Integer.MAX_VALUE).add("longmin", Long.MIN_VALUE)
+          .add("longmax", Long.MAX_VALUE)
+          .add("bigdecimal", BigDecimal.valueOf(123456789.123456789))
+          .add("biginteger", new BigInteger("123456789"))
+          .add("string", "string1").add("false2", false).add("true2", true)
+          .addNull("null2").add("object", object).add("array", array).build();
+
+      Map<String, JsonValue> actMap = myJsonObject;
+      JSONP_Util.dumpMap(actMap, "Actual Map");
+      System.out.println(
+          "Compare actual Map of JsonObject values with expected Map of JsonObject values");
+      pass = JSONP_Util.assertEqualsMap(expMap, actMap);
+    } catch (Exception e) {
+      throw new Fault("jsonObjectTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonObjectTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonObjectTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:86; JSONP:JAVADOC:58; JSONP:JAVADOC:61;
+   * JSONP:JAVADOC:64; JSONP:JAVADOC:67; JSONP:JAVADOC:70; JSONP:JAVADOC:73;
+   * JSONP:JAVADOC:76; JSONP:JAVADOC:80; JSONP:JAVADOC:185; JSONP:JAVADOC:400;
+   * JSONP:JAVADOC:401; JSONP:JAVADOC:402; JSONP:JAVADOC:403; JSONP:JAVADOC:404;
+   * JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409;
+   * 
+   * @test_Strategy: Tests JsonObject/JsonObjectBuilder API's. Build a
+   * JsonObject using the JsonObjectBuilder API's. Write the JsonObject to a
+   * JsonWriter and read it back using a JsonReader. Verify that JsonObject
+   * written to the JsonWriter and then read back using the JsonReader are
+   * equal.
+   */
+  @Test
+  public void jsonObjectTest2() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      System.out.println(
+          "Create JsonObject 'myJsonObject1' using all JsonObjectBuilder API's");
+      JsonObject myJsonObject1 = Json.createObjectBuilder()
+          .add("false", JsonValue.FALSE).add("true", JsonValue.TRUE)
+          .add("null", JsonValue.NULL).add("doublemin", Double.MIN_VALUE)
+          .add("doublemax", Double.MAX_VALUE).add("intmin", Integer.MIN_VALUE)
+          .add("intmax", Integer.MAX_VALUE).add("longmin", Long.MIN_VALUE)
+          .add("longmax", Long.MAX_VALUE)
+          .add("bigdecimal", BigDecimal.valueOf(123456789.123456789))
+          .add("biginteger", new BigInteger("123456789"))
+          .add("string", "string1").add("false2", false).add("true2", true)
+          .addNull("null2").add("object", object).add("array", array).build();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      StringWriter sw = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sw)) {
+        writer.writeObject(myJsonObject1);
+        System.out.println("Close JsonWriter");
+      }
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sw.toString();
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+      System.out.println(
+          "Read the JsonObject back into 'myJsonObject2' using a JsonReader");
+      JsonObject myJsonObject2;
+      try (JsonReader reader = Json.createReader(new StringReader(contents))) {
+        myJsonObject2 = reader.readObject();
+        System.out.println("Save contents of the JsonReader as a String");
+        contents = reader.toString();
+      }
+      System.out.println("Dump contents of JsonReader as a String");
+      System.out.println("JsonReaderContents=" + contents);
+
+      System.out.println("Compare myJsonObject1 and myJsonObject2 for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(myJsonObject1, myJsonObject2);
+    } catch (Exception e) {
+      throw new Fault("jsonObjectTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonObjectTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonObjectTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:58; JSONP:JAVADOC:61; JSONP:JAVADOC:64;
+   * JSONP:JAVADOC:67; JSONP:JAVADOC:70; JSONP:JAVADOC:73; JSONP:JAVADOC:76;
+   * JSONP:JAVADOC:80; JSONP:JAVADOC:86; JSONP:JAVADOC:215; JSONP:JAVADOC:101;
+   * JSONP:JAVADOC:403; JSONP:JAVADOC:264; JSONP:JAVADOC:265; JSONP:JAVADOC:436;
+   * JSONP:JAVADOC:400; JSONP:JAVADOC:401; JSONP:JAVADOC:402; JSONP:JAVADOC:404;
+   * JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409; JSONP:JAVADOC:439;
+   * JSONP:JAVADOC:441; JSONP:JAVADOC:443; JSONP:JAVADOC:527; JSONP:JAVADOC:529;
+   * JSONP:JAVADOC:531; JSONP:JAVADOC:533; JSONP:JAVADOC:539;
+   * 
+   * @test_Strategy: Tests JsonObject/JsonObjectBuilder API's. Build a
+   * JsonObject using the JsonObjectBuilder API's. Verify contents of JsonObject
+   * using JsonObject().getJsonArray(String),
+   * JsonObject().getJsonNumber(String), JsonObject().getJsonObject(String),
+   * JsonObject().getJsonString(String), JsonObject.getInt(String) and
+   * JsonObject.getString(String), JsonObject.getBoolean(String),
+   * JsonObject.getBoolean(String, boolean), JsonObject.getInt(String, int),
+   * JsonObject.getString(String, String)
+   */
+  @Test
+  public void jsonObjectTest3() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject object = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray array = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create myObject JsonObject with 22 name/value pairs");
+      JsonObject myObject = Json.createObjectBuilder().add("key0", -1)
+          .add("key1", +1).add("key2", 1).add("key3", -1e3).add("key4", +1e3)
+          .add("key5", 1e3).add("key6", -2E3).add("key7", +2E3).add("key8", 2E3)
+          .add("key9", Long.MAX_VALUE).add("key10", Long.MIN_VALUE)
+          .add("key11", Integer.MAX_VALUE).add("key12", Integer.MIN_VALUE)
+          .add("key13", Double.MAX_VALUE).add("key14", Double.MIN_VALUE)
+          .add("key15", BigDecimal.valueOf(123456789.123456789))
+          .add("key16", new BigInteger("123456789"))
+          .add("key17", JsonValue.TRUE).add("key18", JsonValue.FALSE)
+          .add("key19", JsonValue.NULL).add("key20", JSONP_Data.asciiCharacters)
+          .add("key21", false).add("key22", true).addNull("key23")
+          .add("key24", object).add("key25", array).build();
+      System.out.println("Checking intValue of key0 for correctness");
+      if (!JSONP_Util.assertEquals(-1,
+          myObject.getJsonNumber("key0").intValue()))
+        pass = false;
+      System.out.println("key0 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key0").toString());
+      System.out.println("Checking intValue of key1 for correctness");
+      if (!JSONP_Util.assertEquals(1, myObject.getInt("key1")))
+        pass = false;
+      System.out.println("key1 via JsonNumber.toString()=" + myObject.getInt("key1"));
+      System.out.println("Checking intValue of key2 for correctness");
+      if (!JSONP_Util.assertEquals(1,
+          myObject.getJsonNumber("key2").intValue()))
+        pass = false;
+      System.out.println("key2 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key2").toString());
+      System.out.println("Checking intValue of key3 for correctness");
+      if (!JSONP_Util.assertEquals(-1000, myObject.getInt("key3")))
+        pass = false;
+      System.out.println("key3 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key3").toString());
+      System.out.println("Checking intValue of key4 for correctness");
+      if (!JSONP_Util.assertEquals(1000,
+          myObject.getJsonNumber("key4").intValue()))
+        pass = false;
+      System.out.println("key4 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key4").toString());
+      System.out.println("Checking intValue of key5 for correctness");
+      if (!JSONP_Util.assertEquals(1000,
+          myObject.getJsonNumber("key5").intValue()))
+        pass = false;
+      System.out.println("key5 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key5").toString());
+      System.out.println("Checking intValue of key6 for correctness");
+      if (!JSONP_Util.assertEquals(-2000,
+          myObject.getJsonNumber("key6").intValue()))
+        pass = false;
+      System.out.println("key6 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key6").toString());
+      System.out.println("Checking intValue of key7 for correctness");
+      if (!JSONP_Util.assertEquals(2000,
+          myObject.getJsonNumber("key7").intValue()))
+        pass = false;
+      System.out.println("key7 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key7").toString());
+      System.out.println("Checking intValue of key8 for correctness");
+      if (!JSONP_Util.assertEquals(2000,
+          myObject.getJsonNumber("key8").intValue()))
+        pass = false;
+      System.out.println("key8 via JsonNumber.toString()="
+          + myObject.getJsonNumber("key8").toString());
+      System.out.println("Checking longValue of key9 for correctness");
+      if (!JSONP_Util.assertEquals(Long.MAX_VALUE,
+          myObject.getJsonNumber("key9").longValue()))
+        pass = false;
+      System.out.println("LongMax via JsonNumber.toString()="
+          + myObject.getJsonNumber("key9").toString());
+      if (!JSONP_Util.assertEquals("" + Long.MAX_VALUE,
+          myObject.getJsonNumber("key9").toString()))
+        pass = false;
+      System.out.println("Checking longValue of key10 for correctness");
+      if (!JSONP_Util.assertEquals(Long.MIN_VALUE,
+          myObject.getJsonNumber("key10").longValue()))
+        pass = false;
+      System.out.println("LongMin via JsonNumber.toString()="
+          + myObject.getJsonNumber("key10").toString());
+      if (!JSONP_Util.assertEquals("" + Long.MIN_VALUE,
+          myObject.getJsonNumber("key10").toString()))
+        pass = false;
+      System.out.println("Checking intValue of key11 for correctness");
+      if (!JSONP_Util.assertEquals(Integer.MAX_VALUE,
+          myObject.getJsonNumber("key11").intValue()))
+        pass = false;
+      System.out.println("IntMax via JsonNumber.toString()="
+          + myObject.getJsonNumber("key11").toString());
+      if (!JSONP_Util.assertEquals("" + Integer.MAX_VALUE,
+          myObject.getJsonNumber("key11").toString()))
+        pass = false;
+      System.out.println("Checking intValue of key12 for correctness");
+      if (!JSONP_Util.assertEquals(Integer.MIN_VALUE,
+          myObject.getJsonNumber("key12").intValue()))
+        pass = false;
+      System.out.println("IntMin via JsonNumber.toString()="
+          + myObject.getJsonNumber("key12").toString());
+      if (!JSONP_Util.assertEquals("" + Integer.MIN_VALUE,
+          myObject.getJsonNumber("key12").toString()))
+        pass = false;
+      System.out.println("Checking doubleValue of key13 for correctness");
+      if (!JSONP_Util.assertEquals(Double.MAX_VALUE,
+          myObject.getJsonNumber("key13").doubleValue()))
+        pass = false;
+      System.out.println("Checking doubleValue of key14 for correctness");
+      if (!JSONP_Util.assertEquals(Double.MIN_VALUE,
+          myObject.getJsonNumber("key14").doubleValue()))
+        pass = false;
+      System.out.println("Checking bigDecimalValue of key15 for correctness");
+      if (!JSONP_Util.assertEquals(BigDecimal.valueOf(123456789.123456789),
+          myObject.getJsonNumber("key15").bigDecimalValue()))
+        pass = false;
+      System.out.println("Checking bigIntegerValue of key16 for correctness");
+      if (!JSONP_Util.assertEquals(new BigInteger("123456789"),
+          myObject.getJsonNumber("key16").bigIntegerValue()))
+        pass = false;
+      System.out.println("Checking getBoolean of key17 for correctness");
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key17")))
+        pass = false;
+      System.out.println("Checking getBoolean of key18 for correctness");
+      if (!JSONP_Util.assertEquals(false, myObject.getBoolean("key18")))
+        pass = false;
+      System.out.println("Checking isNull of key19 for correctness");
+      if (!JSONP_Util.assertEquals(true, myObject.isNull("key19")))
+        pass = false;
+      System.out.println("Checking getJsonString of key20 for correctness");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myObject.getJsonString("key20").getString()))
+        pass = false;
+      System.out.println("Checking getString of key20 for correctness");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myObject.getString("key20")))
+        pass = false;
+      System.out.println("Checking getBoolean of key21 for correctness");
+      if (!JSONP_Util.assertEquals(false, myObject.getBoolean("key21")))
+        pass = false;
+      System.out.println("Checking getBoolean of key22 for correctness");
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key22")))
+        pass = false;
+      System.out.println("Checking isNull of key23 for correctness");
+      if (!JSONP_Util.assertEquals(true, myObject.isNull("key23")))
+        pass = false;
+      System.out.println("Checking getJsonObject of key24 for correctness");
+      if (!JSONP_Util.assertEqualsJsonObjects(object,
+          myObject.getJsonObject("key24")))
+        pass = false;
+      System.out.println("Checking getJsonArray of key25 for correctness");
+      if (!JSONP_Util.assertEqualsJsonArrays(array,
+          myObject.getJsonArray("key25")))
+        pass = false;
+
+      // Verify calls to JsonObject.getBoolean(int)
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key17")))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myObject.getBoolean("key18")))
+        pass = false;
+
+      // Verify calls to JsonObject.getBoolean(String, boolean)
+      System.out.println(
+          "Testing JsonObject.getBoolean(String, boolean) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key17", false)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(false, myObject.getBoolean("key18", true)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key0", true)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key19", true)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key20", true)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key24", true)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(true, myObject.getBoolean("key25", true)))
+        pass = false;
+
+      // Verify calls to JsonObject.getInt(String, int)
+      System.out.println(
+          "Testing JsonObject.getInt(String, int) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(-1, myObject.getInt("key0", 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myObject.getInt("key18", 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myObject.getInt("key19", 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myObject.getInt("key20", 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myObject.getInt("key24", 10)))
+        pass = false;
+      if (!JSONP_Util.assertEquals(10, myObject.getInt("key25", 10)))
+        pass = false;
+
+      // Verify calls to JsonObject.getString(String, String)
+      System.out.println(
+          "Testing JsonObject.getString(String, String) with/without default value setting.");
+      if (!JSONP_Util.assertEquals(JSONP_Data.asciiCharacters,
+          myObject.getString("key20", "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myObject.getString("key0", "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myObject.getString("key18", "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myObject.getString("key19", "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myObject.getString("key24", "foo")))
+        pass = false;
+      if (!JSONP_Util.assertEquals("foo", myObject.getString("key25", "foo")))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonObjectTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonObjectTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonObjectTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:61; JSONP:JAVADOC:70; JSONP:JAVADOC:58;
+   * JSONP:JAVADOC:400; JSONP:JAVADOC:401; JSONP:JAVADOC:403; JSONP:JAVADOC:404;
+   * JSONP:JAVADOC:406; JSONP:JAVADOC:408; JSONP:JAVADOC:409; JSONP:JAVADOC:438;
+   * 
+   * @test_Strategy: Build a JsonObject and than write the JsonObject. Compare
+   * the Json text from the writer contents with the expected Json text output
+   * expected based on the JsonObject.
+   */
+  @Test
+  public void jsonObjectTest4() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      StringWriter sw = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sw)) {
+        writer.writeObject(myJsonObject1);
+        System.out.println("Close JsonWriter");
+      }
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sw.toString();
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+      System.out.println("Remove whitespace from contents.");
+      String actJsonText = JSONP_Util.removeWhitespace(contents);
+      System.out.println(
+          "Compare expected JsonObject text with actual JsonObject text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONOBJECT_TEXT, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("jsonObjectTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonObjectTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonObjectExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:43; JSONP:JAVADOC:47; JSONP:JAVADOC:50;
+   * JSONP:JAVADOC:344; JSONP:JAVADOC:345; JSONP:JAVADOC:79; JSONP:JAVADOC:437;
+   * JSONP:JAVADOC:440; JSONP:JAVADOC:442; JSONP:JAVADOC:528; JSONP:JAVADOC:530;
+   * JSONP:JAVADOC:532; JSONP:JAVADOC:534; JSONP:JAVADOC:540;
+   * 
+   * @test_Strategy: Test JSON exception conditions. Trips the exceptions:
+   * java.lang.ArithmeticException java.lang.ClassCastException
+   * java.lang.NumberFormatException java.lang.UnsupportedOperationException
+   * java.lang.NullPointerException
+   */
+  @Test
+  public void jsonObjectExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonObject testObject = null;
+    JsonArray testArray = null;
+
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      testObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create sample JsonArray for testing");
+      testArray = JSONP_Util.createSampleJsonArray();
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonNumber via getJsonNumber(String)");
+      JsonNumber value = testObject.getJsonNumber("address");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonString via getJsonString(String)");
+      JsonString value = testObject.getJsonString("address");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to JsonArray via getJsonArray(String)");
+      JsonArray value = testObject.getJsonArray("address");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonNumber via getNumber(String)");
+      JsonNumber value = testObject.getJsonNumber("phoneNumber");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonString via getJsonString(String)");
+      JsonString value = testObject.getJsonString("phoneNumber");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to String via getString(String)");
+      String value = testObject.getString("address");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to String via getString(String)");
+      String value = testObject.getString("phoneNumber");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonObject to int via getInt(String)");
+      int value = testObject.getInt("address");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to int via getInt(String)");
+      int value = testObject.getInt("phoneNumber");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonArray to JsonObject via getJsonObject(String)");
+      JsonObject value = testObject.getJsonObject("phoneNumber");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonNumber to JsonString via getJsonString(String)");
+      JsonString value = testObject.getJsonString("age");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonNumber to JsonObject via getJsonNumber(String)");
+      JsonObject value = testObject.getJsonObject("age");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonNumber to JsonArray via getJsonArray(String)");
+      JsonArray value = testObject.getJsonArray("age");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to JsonNumber via getJsonNumber(String)");
+      JsonNumber value = testObject.getJsonNumber("firstName");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to int via getInt(String)");
+      int value = testObject.getInt("firstName");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to JsonObject via getJsonString(String)");
+      JsonObject value = testObject.getJsonObject("firstName");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonString to JsonArray via getJsonArray(String)");
+      JsonArray value = testObject.getJsonArray("firstName");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonValue.FALSE to JsonNumber via getJsonNumber(String)");
+      JsonNumber value = testObject.getJsonNumber("elderly");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonValue.FALSE to JsonString via getJsonString(String)");
+      JsonString value = testObject.getJsonString("elderly");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonValue.FALSE to JsonObject via getJsonObject(String)");
+      JsonObject value = testObject.getJsonObject("elderly");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a JsonValue.FALSE to JsonArray via getJsonArray(String)");
+      JsonArray value = testObject.getJsonArray("elderly");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a non JsonValue.FALSE|JsonValue.TRUE to boolean via getBoolean(String)");
+      boolean value = testObject.getBoolean("firstName");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a non JsonValue.FALSE|JsonValue.TRUE to boolean via getBoolean(String)");
+      boolean value = testObject.getBoolean("age");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a non JsonValue.FALSE|JsonValue.TRUE to boolean via getBoolean(String)");
+      boolean value = testObject.getBoolean("objectOfFooBar");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip ClassCastException
+    try {
+      System.out.println(
+          "Trip ClassCastException trying to cast a non JsonValue.FALSE|JsonValue.TRUE to boolean via getBoolean(String)");
+      boolean value = testObject.getBoolean("arrayOfFooBar");
+      pass = false;
+      System.err.println("Failed to throw ClassCastException");
+    } catch (ClassCastException e) {
+      System.out.println("Got expected ClassCastException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Testing NumberFormatException calling add(String, Double.NaN)
+    try {
+      System.out.println("Trip NumberFormatException calling add(String, Double.NaN)");
+      JsonObject object = Json.createObjectBuilder().add("double", Double.NaN)
+          .build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Testing NumberFormatException calling add(String,
+    // Double.NEGATIVE_INFINITY)
+    try {
+      System.out.println(
+          "Trip NumberFormatException calling add(String, Double.NEGATIVE_INFINITY)");
+      JsonObject object = Json.createObjectBuilder()
+          .add("double", Double.NEGATIVE_INFINITY).build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Testing NumberFormatException calling add(String,
+    // Double.POSITIVE_INFINITY)
+    try {
+      System.out.println(
+          "Trip NumberFormatException calling add(String, Double.POSITIVE_INFINITY)");
+      JsonObject object = Json.createObjectBuilder()
+          .add("double", Double.POSITIVE_INFINITY).build();
+      pass = false;
+      System.err.println("Failed to throw NumberFormatException");
+    } catch (NumberFormatException e) {
+      System.out.println("Got expected NumberFormatException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(\"number\", 12345.12345) and attempting to extract as an exact integer value");
+      JsonObject object = Json.createObjectBuilder().add("number", 12345.12345)
+          .build();
+      System.out.println("Call JsonObject.getJsonNumber(\"number\").intValueExact()");
+      int value = object.getJsonNumber("number").intValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(\"number\", 12345.12345) and attempting to extract as an exact long value");
+      JsonObject object = Json.createObjectBuilder().add("number", 12345.12345)
+          .build();
+      System.out.println("Call JsonObject.getJsonNumber(\"number\").longValueExact()");
+      long value = object.getJsonNumber("number").longValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Test for ArithmeticException
+    try {
+      System.out.println(
+          "Trip ArithmeticException calling add(\"number\", 12345.12345) and attempting to extract as an exact biginteger value");
+      JsonObject object = Json.createObjectBuilder().add("number", 12345.12345)
+          .build();
+      System.out.println(
+          "Call JsonObject.getJsonNumber(\"number\").bigIntegerValueExact()");
+      BigInteger value = object.getJsonNumber("number").bigIntegerValueExact();
+      pass = false;
+      System.err.println("Failed to throw ArithmeticException");
+    } catch (ArithmeticException e) {
+      System.out.println("Got expected ArithmeticException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Tests for UnsupportedOperationException using Collection methods to
+    // modify JsonObject Map
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonObject.put(K,V) trying to modify JsonObject map which should be immutable");
+      testObject.put("foo", JsonValue.FALSE);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonObject.putAll(Map) trying to modify JsonObject map which should be immutable");
+      testObject.putAll(testObject);
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonObject.clear() trying to modify JsonObject map which should be immutable");
+      testObject.clear();
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip UnsupportedOperationException
+    try {
+      System.out.println(
+          "Trip UnsupportedOperationException JsonObject.remove(K) trying to modify JsonObject map which should be immutable");
+      testObject.remove("firstName");
+      pass = false;
+      System.err.println("Failed to throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got expected UnsupportedOperationException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.getBoolean(String) when no mapping exists for name.");
+      boolean value = testObject.getBoolean("foo");
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.getInt(String) when no mapping exists for name.");
+      int value = testObject.getInt("foo");
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.getString(String) when no mapping exists for name.");
+      String value = testObject.getString("foo");
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.isNull(String) when no mapping exists for name.");
+      boolean value = testObject.isNull("foo");
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonObjectExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonObjectNullNameValueExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:561; JSONP:JAVADOC:562; JSONP:JAVADOC:563;
+   * JSONP:JAVADOC:564; JSONP:JAVADOC:565; JSONP:JAVADOC:566; JSONP:JAVADOC:567;
+   * JSONP:JAVADOC:568; JSONP:JAVADOC:569; JSONP:JAVADOC:570; JSONP:JAVADOC:571;
+   * 
+   * @test_Strategy: Test JSON NPE exception conditions when attempting to add a
+   * specified name or value that is null.
+   */
+  @Test
+  public void jsonObjectNullNameValueExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonObjectBuilder job = Json.createObjectBuilder();
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, JsonValue) when name is null.");
+      job.add(null, JsonValue.TRUE);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, JsonValue) when value is null.");
+      job.add("name", (JsonValue) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, String) when name is null.");
+      job.add(null, "value");
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, String) when value is null.");
+      job.add("name", (String) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, BigInteger) when name is null.");
+      job.add(null, new BigInteger("123456789"));
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, BigInteger) when value is null.");
+      job.add("name", (BigInteger) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, BigDecimal) when name is null.");
+      job.add(null, new BigDecimal("123456789"));
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, BigDecimal) when value is null.");
+      job.add("name", (BigDecimal) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, int) when name is null.");
+      job.add(null, 123456789);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, long) when name is null.");
+      job.add(null, 123456789L);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, double) when name is null.");
+      job.add(null, 123456.789);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.add(String, boolean) when name is null.");
+      job.add(null, true);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObject.addNull(String) when name is null.");
+      job.addNull(null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, JsonObjectBuilder) when name is null.");
+      job.add(null, Json.createObjectBuilder());
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonObjectBuilder.add(String, JsonObjectBuilder) when value is null.");
+      job.add("name", (JsonObjectBuilder) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(String, JsonArrayBuilder) when name is null.");
+      job.add(null, Json.createArrayBuilder());
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NullPointerException
+    try {
+      System.out.println(
+          "Trip NullPointerException for JsonArrayBuilder.add(String, JsonArrayBuilder) when value is null.");
+      job.add("name", (JsonArrayBuilder) null);
+      pass = false;
+      System.err.println("Failed to throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonObjectNullNameValueExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonCreateObjectBuilder11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:577; JSONP:JAVADOC:578; JSONP:JAVADOC:656;
+   * JSONP:JAVADOC:657;
+   *
+   * @test_Strategy: Tests JsonObjectBuilder API factory methods added in JSON-P
+   * 1.1.
+   */
+  @Test
+  public void jsonCreateObjectBuilder11Test() throws Fault {
+    CreateObjectBuilder createTest = new CreateObjectBuilder();
+    final TestResult result = createTest.test();
+    result.eval();
+  }
+
+  /*
+   * @testName: jsonObjectBuilder11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:618; JSONP:JAVADOC:619;
+   * 
+   * @test_Strategy: Tests JsonObjectBuilder API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonObjectBuilder11Test() throws Fault {
+    ObjectBuild buildTest = new ObjectBuild();
+    final TestResult result = buildTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/CreateObjectBuilder.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/CreateObjectBuilder.java
new file mode 100644
index 0000000..f4b370c
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/CreateObjectBuilder.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonobjecttests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.util.HashMap;
+import java.util.Map;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests:
+ * {@link JsonObjectBuilder} API factory methods added in JSON-P 1.1.<br>
+ */
+public class CreateObjectBuilder {
+
+  /**
+   * Test {@link JsonObjectBuilder} factory method added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonObjectBuilder API factory methods added in JSON-P 1.1.");
+    System.out.println("JsonObjectBuilder API factory methods added in JSON-P 1.1.");
+    testCreateFromMap(result);
+    testCreateFromJsonObject(result);
+    return result;
+  }
+
+  /**
+   * Test {@link Json#createObjectBuilder(Map<String,Object>)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateFromMap(final TestResult result) {
+    System.out.println(" - Json#createObjectBuilder(Map<String,Object>)");
+    final JsonObject check = createSimpleObjectWithStr();
+    Map<String, Object> values = new HashMap<>(2);
+    values.put(DEF_NAME, DEF_VALUE);
+    values.put(STR_NAME, STR_VALUE);
+    final JsonObjectBuilder builder = Json.createObjectBuilder(values);
+    final JsonObject out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("createObjectBuilder(Map<String,Object>)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test {@link Json#createObjectBuilder(JsonObject)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateFromJsonObject(final TestResult result) {
+    System.out.println(" - Json#createObjectBuilder(JsonObject)");
+    final JsonObject check = createSimpleObjectWithStr();
+    final JsonObjectBuilder builder = Json.createObjectBuilder(check);
+    final JsonObject out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("reateObjectBuilder(JsonObject)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ObjectBuild.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ObjectBuild.java
new file mode 100644
index 0000000..88f7127
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonobjecttests/ObjectBuild.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonobjecttests;
+
+import jakarta.jsonp.tck.api.common.ObjectBuilder;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for {@link JsonObject}
+ * and {@link JsonObjectBuilder}.
+ */
+public class ObjectBuild {
+
+  /**
+   * {@link JsonArrayBuilder} API remove() methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonObjectBuilder API methods added in JSON-P 1.1.");
+    System.out.println("JsonObjectBuilder API methods added in JSON-P 1.1.");
+    testAddString(result);
+    testAddInt(result);
+    testAddBool(result);
+    testAddObject(result);
+    testAddAllNull(result);
+    testRemoveString(result);
+    testRemoveInt(result);
+    testRemoveBool(result);
+    testRemoveObject(result);
+    testRemoveNull(result);
+    return result;
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonObjectBuilder)} method on
+   * {@code String} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddString(final TestResult result) {
+    System.out.println(" - addAll(JsonObjectBuilder) for String");
+    final JsonObjectBuilder target = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE);
+    final JsonObjectBuilder arg = ObjectBuilder.add(Json.createObjectBuilder(),
+        STR_NAME, STR_VALUE);
+    final JsonObject check = createSimpleObjectWithStr();
+    verifyAddAll(result, check, target, arg);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonObjectBuilder)} method on
+   * {@code int} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddInt(final TestResult result) {
+    System.out.println(" - addAll(JsonObjectBuilder) for int");
+    final JsonObjectBuilder target = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE);
+    final JsonObjectBuilder arg = ObjectBuilder.add(Json.createObjectBuilder(),
+        INT_NAME, INT_VALUE);
+    final JsonObject check = createSimpleObjectWithInt();
+    verifyAddAll(result, check, target, arg);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonObjectBuilder)} method on
+   * {@code boolean} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddBool(final TestResult result) {
+    System.out.println(" - addAll(JsonObjectBuilder) for boolean");
+    final JsonObjectBuilder target = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE);
+    final JsonObjectBuilder arg = ObjectBuilder.add(Json.createObjectBuilder(),
+        BOOL_NAME, BOOL_VALUE);
+    final JsonObject check = createSimpleObjectWithBool();
+    verifyAddAll(result, check, target, arg);
+  }
+
+  /**
+   * Test {@code JsonArrayBuilder addAll(JsonObjectBuilder)} method on
+   * {@code JsonObject} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddObject(final TestResult result) {
+    System.out.println(" - addAll(JsonObjectBuilder) for JsonObject");
+    final JsonObjectBuilder target = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE);
+    final JsonObjectBuilder arg = ObjectBuilder.add(Json.createObjectBuilder(),
+        OBJ_NAME, OBJ_VALUE);
+    final JsonObject check = createCompoundObjectWithObject();
+    verifyAddAll(result, check, target, arg);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder addAll(JsonObjectBuilder)} method with
+   * {@code null} builder argument.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAddAllNull(final TestResult result) {
+    System.out.println(" - addAll(JsonObjectBuilder) for null builder argument");
+    JsonObjectBuilder builder = Json.createObjectBuilder();
+    try {
+      builder.addAll((JsonObjectBuilder) null);
+      result.fail("addAll(null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("addAll(null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder remove(String)} method on {@code String}
+   * value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveString(final TestResult result) {
+    System.out.println(" - remove(String) for String");
+    final JsonObjectBuilder in = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE)
+        .add(STR_NAME, STR_VALUE);
+    final JsonObjectBuilder builder = in.remove(STR_NAME);
+    final JsonObject check = createSimpleObject();
+    verifyRemovel(result, check, builder);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder remove(String)} method on {@code int} value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveInt(final TestResult result) {
+    System.out.println(" - remove(String) for int");
+    final JsonObjectBuilder in = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE)
+        .add(INT_NAME, INT_VALUE);
+    final JsonObjectBuilder builder = in.remove(INT_NAME);
+    final JsonObject check = createSimpleObject();
+    verifyRemovel(result, check, builder);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder remove(String)} method on {@code boolean}
+   * value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveBool(final TestResult result) {
+    System.out.println(" - remove(String) for boolean");
+    final JsonObjectBuilder in = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE)
+        .add(BOOL_NAME, BOOL_VALUE);
+    final JsonObjectBuilder builder = in.remove(BOOL_NAME);
+    final JsonObject check = createSimpleObject();
+    verifyRemovel(result, check, builder);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder remove(String)} method on {@code JsonObject}
+   * value.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveObject(final TestResult result) {
+    System.out.println(" - remove(String) for JsonObject");
+    final JsonObjectBuilder in = ObjectBuilder
+        .add(Json.createObjectBuilder(), DEF_NAME, DEF_VALUE)
+        .add(DEF_OBJ_NAME, DEF_OBJ_VALUE).add(OBJ_NAME, OBJ_VALUE);
+    final JsonObjectBuilder builder = in.remove(OBJ_NAME);
+    final JsonObject check = createCompoundObject();
+    verifyRemovel(result, check, builder);
+  }
+
+  /**
+   * Test {@code JsonObjectBuilder remove(String)} method with {@code null}
+   * name.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testRemoveNull(final TestResult result) {
+    System.out.println(" - remove(String) for null name argument");
+    JsonObjectBuilder builder = Json.createObjectBuilder();
+    try {
+      builder.remove((String) null);
+      result.fail("remove(null)",
+          "Calling method with null argument shall throw NullPointerException");
+    } catch (NullPointerException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    } catch (Throwable t) {
+      result.fail("remove(null)",
+          "Calling method with null argument shall throw NullPointerException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test helper: Verify {@code JsonObjectBuilder addAll(JsonObjectBuilder)}
+   * method on provided builders.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param target
+   *          Builder instance used to call {@code addAll(JsonObjectBuilder)}
+   *          method.
+   * @param arg
+   *          Builder instance passed as an argument.
+   */
+  private void verifyAddAll(final TestResult result, final JsonObject check,
+      JsonObjectBuilder target, final JsonObjectBuilder arg) {
+    target.addAll(arg);
+    final JsonObject out = target.build();
+    if (operationFailed(check, out)) {
+      result.fail("addAll(JsonObjectBuilder)", "Output builder "
+          + valueToString(out) + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Test helper: Verify {@code JsonObjectBuilder remove(String)} method on
+   * provided builder.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param check
+   *          Expected value (used for operation check).
+   * @param builder
+   *          Builder instance after {@code JsonObjectBuilder remove(String)}
+   *          method was called.
+   * @param arg
+   *          Builder instance passed as an argument.
+   */
+  private void verifyRemovel(final TestResult result, final JsonObject check,
+      JsonObjectBuilder builder) {
+    final JsonObject out = builder.build();
+    if (operationFailed(check, out)) {
+      result.fail("remove(String)", "Output builder " + valueToString(out)
+          + " value shall be " + valueToString(check));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsereventtests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsereventtests/ClientTests.java
new file mode 100644
index 0000000..2da56f5
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsereventtests/ClientTests.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonparsereventtests;
+
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonValueOfTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:128;
+   * 
+   * @test_Strategy: Test JsonParser.Event.valueOf() API method call with all
+   * JsonParser.Event types.
+   *
+   */
+  @Test
+  public void jsonValueOfTest() throws Fault {
+    boolean pass = true;
+
+    String eventTypeStrings[] = { "END_ARRAY", "END_OBJECT", "KEY_NAME",
+        "START_ARRAY", "START_OBJECT", "VALUE_FALSE", "VALUE_NULL",
+        "VALUE_NUMBER", "VALUE_STRING", "VALUE_TRUE" };
+
+    for (String eventTypeString : eventTypeStrings) {
+      JsonParser.Event eventType;
+      try {
+        System.out.println(
+            "Testing enum value for string constant name " + eventTypeString);
+        eventType = JsonParser.Event.valueOf(eventTypeString);
+        System.out.println("Got enum type " + eventType + " for enum string constant named "
+            + eventTypeString);
+      } catch (Exception e) {
+        System.err.println("Caught unexpected exception: " + e);
+        pass = false;
+      }
+
+    }
+
+    System.out.println("Testing negative test case for NullPointerException");
+    try {
+      JsonParser.Event.valueOf(null);
+      System.err.println("did not get expected NullPointerException");
+      pass = false;
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      System.err.println("Got unexpected exception " + e);
+      pass = false;
+    }
+
+    System.out.println("Testing negative test case for IllegalArgumentException");
+    try {
+      JsonParser.Event.valueOf("INVALID");
+      System.err.println("did not get expected IllegalArgumentException");
+      pass = false;
+    } catch (IllegalArgumentException e) {
+      System.out.println("Got expected IllegalArgumentException");
+    } catch (Exception e) {
+      System.err.println("Got unexpected exception " + e);
+      pass = false;
+    }
+
+    if (!pass)
+      throw new Fault("jsonValueOfTest Failed");
+  }
+
+  /*
+   * @testName: jsonValuesTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:129;
+   * 
+   * @test_Strategy: Test JsonParser.Event.values() API method call and verify
+   * enums returned.
+   *
+   */
+  @Test
+  public void jsonValuesTest() throws Fault {
+    boolean pass = true;
+
+    System.out.println(
+        "Testing API method JsonParser.Event.values() to return array of enums.");
+    JsonParser.Event[] values = JsonParser.Event.values();
+
+    for (JsonParser.Event eventType : values) {
+      String eventString = JSONP_Util.getEventTypeString(eventType);
+      if (eventString == null) {
+        System.err.println("Got no value for enum " + eventType);
+        pass = false;
+      } else
+        System.out.println("Got " + eventString + " for enum " + eventType);
+    }
+
+    if (!pass)
+      throw new Fault("jsonValuesTest Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparserfactorytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparserfactorytests/ClientTests.java
new file mode 100644
index 0000000..d8a2b7a
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparserfactorytests/ClientTests.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id: Client.java 66863 2012-07-23 11:26:40Z adf $
+ */
+package jakarta.jsonp.tck.api.jsonparserfactorytests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+import java.io.*;
+import java.nio.charset.Charset;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ArrayList;
+
+import jakarta.json.stream.JsonParser.Event.*;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonParserFactoryTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:164;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * JsonParser parser1 = parserFactory.createParser(Reader) JsonParser parser2
+   * = parserFactory.createParser(Reader)
+   */
+  @Test
+  public void jsonParserFactoryTest1() throws Fault {
+    boolean pass = true;
+    JsonParser parser1 = null;
+    JsonParser parser2 = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("Create JsonParserFactory with a configuration");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("--------------------------------------------------");
+      System.out.println("TEST CASE [JsonParserFactory.createParser(Reader)]");
+      System.out.println("--------------------------------------------------");
+      String jsonObjectString = "{\"foo\":\"bar\"}";
+      System.out.println("Create 1st JsonParser from the Reader using JsonParserFactory");
+      parser1 = parserFactory.createParser(new StringReader(jsonObjectString));
+      if (parser1 == null) {
+        System.err.println("ParserFactory failed to create parser1 from Reader");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser1, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser1, "foo", "bar");
+        JSONP_Util.testEventType(parser1, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+      System.out.println("Create 2nd JsonParser from the Reader using JsonParserFactory");
+      parser2 = parserFactory.createParser(new StringReader(jsonObjectString));
+      if (parser2 == null) {
+        System.err.println("ParserFactory failed to create parser2 from Reader");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser2, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser2, "foo", "bar");
+        JSONP_Util.testEventType(parser2, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest1 Failed: ", e);
+    } finally {
+      try {
+        parser1.close();
+        parser2.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:166;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * JsonParser parser1 = parserFactory.createParser(JsonObject) JsonParser
+   * parser2 = parserFactory.createParser(JsonObject)
+   */
+  @Test
+  public void jsonParserFactoryTest2() throws Fault {
+    boolean pass = true;
+    JsonParser parser1 = null;
+    JsonParser parser2 = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("Create JsonParserFactory with a configuration");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("------------------------------------------------------");
+      System.out.println("TEST CASE [JsonParserFactory.createParser(JsonObject)]");
+      System.out.println("------------------------------------------------------");
+      String jsonObjectString = "{\"foo\":\"bar\"}";
+      JsonObject jsonObj = JSONP_Util
+          .createJsonObjectFromString(jsonObjectString);
+      System.out.println(
+          "Create 1st JsonParser from the JsonObject using JsonParserFactory");
+      parser1 = parserFactory.createParser(jsonObj);
+      if (parser1 == null) {
+        System.err.println("ParserFactory failed to create parser1 from JsonObject");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser1, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser1, "foo", "bar");
+        JSONP_Util.testEventType(parser1, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+      System.out.println(
+          "Create 2nd JsonParser from the JsonObject using JsonParserFactory");
+      parser2 = parserFactory.createParser(jsonObj);
+      if (parser2 == null) {
+        System.err.println("ParserFactory failed to create parser2 from JsonObject");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser2, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser2, "foo", "bar");
+        JSONP_Util.testEventType(parser2, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest2 Failed: ", e);
+    } finally {
+      try {
+        parser1.close();
+        parser2.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:167;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * JsonParser parser1 = parserFactory.createParser(JsonArray) JsonParser
+   * parser2 = parserFactory.createParser(JsonArray)
+   */
+  @Test
+  public void jsonParserFactoryTest3() throws Fault {
+    boolean pass = true;
+    JsonParser parser1 = null;
+    JsonParser parser2 = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("Create JsonParserFactory with a configuration");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-----------------------------------------------------");
+      System.out.println("TEST CASE [JsonParserFactory.createParser(JsonArray)]");
+      System.out.println("-----------------------------------------------------");
+      String jsonArrayString = "[\"foo\",\"bar\"]";
+      JsonArray jsonArr = JSONP_Util.createJsonArrayFromString(jsonArrayString);
+      System.out.println(
+          "Create 1st JsonParser from the JsonArray using JsonParserFactory");
+      parser1 = parserFactory.createParser(jsonArr);
+      if (parser1 == null) {
+        System.err.println("ParserFactory failed to create parser1 from JsonArray");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonArrayString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser1, JsonParser.Event.START_ARRAY);
+        JSONP_Util.testStringValue(parser1, "foo");
+        JSONP_Util.testStringValue(parser1, "bar");
+        JSONP_Util.testEventType(parser1, JsonParser.Event.END_ARRAY);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+      System.out.println(
+          "Create 2nd JsonParser from the JsonArray using JsonParserFactory");
+      parser2 = parserFactory.createParser(jsonArr);
+      if (parser2 == null) {
+        System.err.println("ParserFactory failed to create parser2 from JsonArray");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonArrayString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser2, JsonParser.Event.START_ARRAY);
+        JSONP_Util.testStringValue(parser2, "foo");
+        JSONP_Util.testStringValue(parser2, "bar");
+        JSONP_Util.testEventType(parser2, JsonParser.Event.END_ARRAY);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest3 Failed: ", e);
+    } finally {
+      try {
+        parser1.close();
+        parser2.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:165;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * JsonParser parser1 = parserFactory.createParser(InputStream) JsonParser
+   * parser2 = parserFactory.createParser(InputStream)
+   */
+  @Test
+  public void jsonParserFactoryTest4() throws Fault {
+    boolean pass = true;
+    JsonParser parser1 = null;
+    JsonParser parser2 = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("Create JsonParserFactory with a configuration");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-------------------------------------------------------");
+      System.out.println("TEST CASE [JsonParserFactory.createParser(InputStream)]");
+      System.out.println("-------------------------------------------------------");
+      String jsonObjectString = "{\"foo\":\"bar\"}";
+      System.out.println(
+          "Create 1st JsonParser from the InputStream using JsonParserFactory");
+      parser1 = parserFactory.createParser(new ByteArrayInputStream(
+          jsonObjectString.getBytes(JSONP_Util.UTF_8)));
+      if (parser1 == null) {
+        System.err.println("ParserFactory failed to create parser1 from InputStream");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser1, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser1, "foo", "bar");
+        JSONP_Util.testEventType(parser1, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+      System.out.println(
+          "Create 2nd JsonParser from the InputStream using JsonParserFactory");
+      parser2 = parserFactory.createParser(new ByteArrayInputStream(
+          jsonObjectString.getBytes(JSONP_Util.UTF_8)));
+      if (parser2 == null) {
+        System.err.println("ParserFactory failed to create parser2 from InputStream");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser2, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser2, "foo", "bar");
+        JSONP_Util.testEventType(parser2, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest4 Failed: ", e);
+    } finally {
+      try {
+        parser1.close();
+        parser2.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:201;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * JsonParser parser1 = parserFactory.createParser(InputStream, Charset)
+   * JsonParser parser2 = parserFactory.createParser(InputStream, Charset)
+   */
+  @Test
+  public void jsonParserFactoryTest5() throws Fault {
+    boolean pass = true;
+    JsonParser parser1 = null;
+    JsonParser parser2 = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("Create JsonParserFactory with a configuration");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println(
+          "----------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [JsonParserFactory.createParser(InputStream, Charset)]");
+      System.out.println(
+          "----------------------------------------------------------------");
+      String jsonObjectString = "{\"foo\":\"bar\"}";
+      System.out.println(
+          "Create 1st JsonParser from the InputStream using JsonParserFactory");
+      parser1 = parserFactory.createParser(
+          new ByteArrayInputStream(jsonObjectString.getBytes(JSONP_Util.UTF_8)),
+          JSONP_Util.UTF_8);
+      if (parser1 == null) {
+        System.err.println("ParserFactory failed to create parser1 from InputStream");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser1, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser1, "foo", "bar");
+        JSONP_Util.testEventType(parser1, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+      System.out.println(
+          "Create 2nd JsonParser from the InputStream using JsonParserFactory");
+      parser2 = parserFactory.createParser(
+          new ByteArrayInputStream(jsonObjectString.getBytes(JSONP_Util.UTF_8)),
+          JSONP_Util.UTF_8);
+      if (parser2 == null) {
+        System.err.println("ParserFactory failed to create parser2 from InputStream");
+        pass = false;
+      } else {
+        System.out.println("Parsing " + jsonObjectString);
+        System.out.println("Verify that JSON Parser Events/Data matches");
+        JSONP_Util.resetParseErrs();
+        JSONP_Util.testEventType(parser2, JsonParser.Event.START_OBJECT);
+        JSONP_Util.testKeyStringValue(parser2, "foo", "bar");
+        JSONP_Util.testEventType(parser2, JsonParser.Event.END_OBJECT);
+        int parseErrs = JSONP_Util.getParseErrs();
+        if (parseErrs != 0) {
+          System.err.println("There were " + parseErrs + " parser errors that occurred.");
+          pass = false;
+        }
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest5 Failed: ", e);
+    } finally {
+      try {
+        parser1.close();
+        parser2.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryTest6
+   * 
+   * @assertion_ids: JSONP:JAVADOC:417; JSONP:JAVADOC:164; JSONP:JAVADOC:428;
+   * 
+   * @test_Strategy: Tests the JsonParserFactory API.
+   *
+   * JsonParserFactory parserFactory = Json.createParserFactory(Map<String, ?>);
+   * Map<String, ?> config = JsonParserFactory.getConfigInUse();
+   *
+   * Test for the following 2 scenarios: 1) no supported provider property
+   * (empty config) 2) non supported provider property
+   */
+  @Test
+  public void jsonParserFactoryTest6() throws Fault {
+    boolean pass = true;
+    JsonParserFactory parserFactory;
+    Map<String, ?> config;
+    try {
+      System.out.println("----------------------------------------------");
+      System.out.println("Test scenario1: no supported provider property");
+      System.out.println("----------------------------------------------");
+      System.out.println("Create JsonParserFactory with Map<String, ?> with EMPTY config");
+      parserFactory = Json.createParserFactory(JSONP_Util.getEmptyConfig());
+      config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-----------------------------------------------");
+      System.out.println("Test scenario2: non supported provider property");
+      System.out.println("-----------------------------------------------");
+      System.out.println("Create JsonParserFactory with Map<String, ?> with FOO config");
+      parserFactory = Json.createParserFactory(JSONP_Util.getFooConfig());
+      config = parserFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserFactoryTest6 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonParserFactoryTest6 Failed");
+  }
+
+  /*
+   * @testName: jsonParserFactoryExceptionTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:225;
+   * 
+   * @test_Strategy: Test JsonParserFactory exception conditions. Trip the
+   * following exception due to unknown encoding or i/o error:
+   *
+   * jakarta.json.JsonException
+   */
+  @Test
+  public void jsonParserFactoryExceptionTest() throws Fault {
+    boolean pass = true;
+
+    // Tests JsonParserFactory.createParser(InputStream) for JsonException if
+    // i/o error
+    try {
+      System.out.println(
+          "Tests JsonParserFactory.createParser(InputStream) for JsonException if i/o error.");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      MyBufferedInputStream mbi = new MyBufferedInputStream(
+          JSONP_Util.getInputStreamFromString("{}"), true);
+      JsonParser parser = parserFactory.createParser(mbi);
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Tests JsonParserFactory.createParser(InputStream) for JsonException if
+    // unknown encoding
+    try {
+      System.out.println(
+          "Tests JsonParserFactory.createParser(InputStream) for JsonException if unknown encoding.");
+      JsonParserFactory parserFactory = Json
+          .createParserFactory(JSONP_Util.getEmptyConfig());
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectUnknownEncoding.json");
+      JsonParser parser = parserFactory.createParser(is);
+      System.out.println("parser=" + parser);
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonParserFactoryExceptionTest Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/ClientTests.java
new file mode 100644
index 0000000..c1a3f0e
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/ClientTests.java
@@ -0,0 +1,1908 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonparsertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /*
+   * Utitity method to parse various JsonObjectUTF encoded files
+   */
+  private boolean parseAndVerify_JsonObjectUTF(JsonParser parser)
+      throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "stringName", "stringValue");
+      JSONP_Util.testKeyStartObjectValue(parser, "objectName");
+      JSONP_Util.testKeyStringValue(parser, "foo", "bar");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStartArrayValue(parser, "arrayName");
+      JSONP_Util.testIntegerValue(parser, 1);
+      JSONP_Util.testIntegerValue(parser, 2);
+      JSONP_Util.testIntegerValue(parser, 3);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonObjectWithAllTypesOfData
+   */
+  private boolean parseAndVerify_JsonObjectWithAllTypesOfData(JsonParser parser)
+      throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "emptyString", "");
+      JSONP_Util.testKeyStartArrayValue(parser, "emptyArray");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testKeyStartObjectValue(parser, "emptyObject");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "string", "string");
+      JSONP_Util.testKeyIntegerValue(parser, "number", 100);
+      JSONP_Util.testKeyTrueValue(parser, "true");
+      JSONP_Util.testKeyFalseValue(parser, "false");
+      JSONP_Util.testKeyNullValue(parser, "null");
+      JSONP_Util.testKeyStartObjectValue(parser, "object");
+      JSONP_Util.testKeyStringValue(parser, "emptyString", "");
+      JSONP_Util.testKeyStartArrayValue(parser, "emptyArray");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testKeyStartObjectValue(parser, "emptyObject");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "string", "string");
+      JSONP_Util.testKeyIntegerValue(parser, "number", 100);
+      JSONP_Util.testKeyTrueValue(parser, "true");
+      JSONP_Util.testKeyFalseValue(parser, "false");
+      JSONP_Util.testKeyNullValue(parser, "null");
+      JSONP_Util.testKeyStartObjectValue(parser, "object");
+      JSONP_Util.testKeyStringValue(parser, "name", "value");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStartArrayValue(parser, "array");
+      JSONP_Util.testStringValue(parser, "one");
+      JSONP_Util.testStringValue(parser, "two");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStartArrayValue(parser, "array");
+      JSONP_Util.testStringValue(parser, "string");
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testTrueValue(parser, JsonParser.Event.VALUE_TRUE);
+      JSONP_Util.testFalseValue(parser, JsonParser.Event.VALUE_FALSE);
+      JSONP_Util.testNullValue(parser, JsonParser.Event.VALUE_NULL);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "name", "value");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "one");
+      JSONP_Util.testStringValue(parser, "two");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testKeyIntegerValue(parser, "intPositive", 100);
+      JSONP_Util.testKeyIntegerValue(parser, "intNegative", -100);
+      JSONP_Util.testKeyLongValue(parser, "longMax", 9223372036854775807L);
+      JSONP_Util.testKeyLongValue(parser, "longMin", -9223372036854775808L);
+      JSONP_Util.testKeyDoubleValue(parser, "fracPositive", (double) 0.5);
+      JSONP_Util.testKeyDoubleValue(parser, "fracNegative", (double) -0.5);
+      JSONP_Util.testKeyDoubleValue(parser, "expPositive1", (double) 7e3);
+      JSONP_Util.testKeyDoubleValue(parser, "expPositive2", (double) 7e+3);
+      JSONP_Util.testKeyDoubleValue(parser, "expPositive3", (double) 9E3);
+      JSONP_Util.testKeyDoubleValue(parser, "expPositive4", (double) 9E+3);
+      JSONP_Util.testKeyDoubleValue(parser, "expNegative1", (double) 7e-3);
+      JSONP_Util.testKeyDoubleValue(parser, "expNegative2", (double) 7E-3);
+      JSONP_Util.testKeyStringValue(parser, "asciiChars",
+          JSONP_Data.asciiCharacters);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonObjectWithLotsOfNestedObjectsData
+   */
+  private boolean parseAndVerify_JsonObjectWithLotsOfNestedObjectsData(
+      JsonParser parser) throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      for (int i = 1; i < 31; i++) {
+        JSONP_Util.testKeyStartObjectValue(parser, "nested" + i);
+        JSONP_Util.testKeyStringValue(parser, "name" + i, "value" + i);
+      }
+      for (int i = 1; i < 31; i++) {
+        JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      }
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonArrayWithLotsOfNestedObjectsData
+   */
+  private boolean parseAndVerify_JsonArrayWithLotsOfNestedObjectsData(
+      JsonParser parser) throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "name1", "value1");
+      for (int i = 2; i < 31; i++) {
+        JSONP_Util.testKeyStartObjectValue(parser, "nested" + i);
+        JSONP_Util.testKeyStringValue(parser, "name" + i, "value" + i);
+      }
+      for (int i = 2; i < 31; i++) {
+        JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      }
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonArrayWithLotsOfNestedArraysData
+   */
+  private boolean parseAndVerify_JsonArrayWithLotsOfNestedArraysData(
+      JsonParser parser) throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "name1");
+      JSONP_Util.testStringValue(parser, "value1");
+      for (int i = 2; i < 31; i++) {
+        JSONP_Util.testStringValue(parser, "nested" + i);
+        JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+        JSONP_Util.testStringValue(parser, "name" + i);
+        JSONP_Util.testStringValue(parser, "value" + i);
+      }
+      for (int i = 2; i < 31; i++) {
+        JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      }
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonArrayWithMultipleArraysData
+   */
+  private boolean parseAndVerify_JsonArrayWithMultipleArraysData(
+      JsonParser parser) throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "string");
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testTrueValue(parser, JsonParser.Event.VALUE_TRUE);
+      JSONP_Util.testFalseValue(parser, JsonParser.Event.VALUE_FALSE);
+      JSONP_Util.testNullValue(parser, JsonParser.Event.VALUE_NULL);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "object", "object");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "one");
+      JSONP_Util.testStringValue(parser, "two");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testDoubleValue(parser, (double) 7e7);
+      JSONP_Util.testTrueValue(parser, JsonParser.Event.VALUE_TRUE);
+      JSONP_Util.testFalseValue(parser, JsonParser.Event.VALUE_FALSE);
+      JSONP_Util.testNullValue(parser, JsonParser.Event.VALUE_NULL);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "object2", "object2");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /*
+   * Utitity method to parse JsonArrayWithAllTypesOfData
+   */
+  private boolean parseAndVerify_JsonArrayWithAllTypesOfData(JsonParser parser)
+      throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "");
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testStringValue(parser, "string");
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testTrueValue(parser, JsonParser.Event.VALUE_TRUE);
+      JSONP_Util.testFalseValue(parser, JsonParser.Event.VALUE_FALSE);
+      JSONP_Util.testNullValue(parser, JsonParser.Event.VALUE_NULL);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "emptyString", "");
+      JSONP_Util.testKeyStartArrayValue(parser, "emptyArray");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testKeyStartObjectValue(parser, "emptyObject");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "string", "string");
+      JSONP_Util.testKeyIntegerValue(parser, "number", 100);
+      JSONP_Util.testKeyTrueValue(parser, "true");
+      JSONP_Util.testKeyFalseValue(parser, "false");
+      JSONP_Util.testKeyNullValue(parser, "null");
+      JSONP_Util.testKeyStartObjectValue(parser, "object");
+      JSONP_Util.testKeyStringValue(parser, "name", "value");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testKeyStartArrayValue(parser, "array");
+      JSONP_Util.testStringValue(parser, "one");
+      JSONP_Util.testStringValue(parser, "two");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "string");
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testTrueValue(parser, JsonParser.Event.VALUE_TRUE);
+      JSONP_Util.testFalseValue(parser, JsonParser.Event.VALUE_FALSE);
+      JSONP_Util.testNullValue(parser, JsonParser.Event.VALUE_NULL);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStringValue(parser, "name", "value");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_ARRAY);
+      JSONP_Util.testStringValue(parser, "one");
+      JSONP_Util.testStringValue(parser, "two");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.testIntegerValue(parser, 100);
+      JSONP_Util.testIntegerValue(parser, -100);
+      JSONP_Util.testLongValue(parser, 9223372036854775807L);
+      JSONP_Util.testLongValue(parser, -9223372036854775808L);
+      JSONP_Util.testDoubleValue(parser, (double) 0.5);
+      JSONP_Util.testDoubleValue(parser, (double) -0.5);
+      JSONP_Util.testDoubleValue(parser, (double) 7e3);
+      JSONP_Util.testDoubleValue(parser, (double) 7e+3);
+      JSONP_Util.testDoubleValue(parser, (double) 9E3);
+      JSONP_Util.testDoubleValue(parser, (double) 9E+3);
+      JSONP_Util.testDoubleValue(parser, (double) 7e-3);
+      JSONP_Util.testDoubleValue(parser, (double) 7E-3);
+      JSONP_Util.testStringValue(parser, JSONP_Data.asciiCharacters);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  private boolean parseAndVerify_JsonHelloWorld(JsonParser parser)
+      throws Exception {
+    boolean pass = true;
+
+    try {
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyStartObjectValue(parser, "greetingObj");
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testKeyStringValue(parser, "hello", "world");
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testKeyStartArrayValue(parser, "greetingArr");
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testStringValue(parser, "hello");
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testStringValue(parser, "world");
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_ARRAY);
+      JSONP_Util.dumpLocation(parser);
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      JSONP_Util.dumpLocation(parser);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+    } catch (Exception e) {
+      throw e;
+    }
+    return pass;
+  }
+
+  /* Tests */
+
+  /*
+   * @testName: jsonParserTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:133; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in "JSONP_Data.jsonObjectWithAllTypesOfData". Creates
+   * the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParser(Reader)
+   */
+  @Test
+  public void jsonParserTest1() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("-------------------------------------");
+      System.out.println("TEST CASE [Json.createParser(Reader)]");
+      System.out.println("-------------------------------------");
+      System.out.println("Create Reader from (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      StringReader reader = new StringReader(
+          JSONP_Data.jsonObjectWithAllTypesOfData);
+      System.out.println("Create JsonParser from the Reader");
+      parser = Json.createParser(reader);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      pass = parseAndVerify_JsonObjectWithAllTypesOfData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest1 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:417; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:166;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in "JSONP_Data.jsonObjectWithAllTypesOfData". Creates
+   * the JsonParser via the API:
+   *
+   * JsonParser parser =
+   * Json.createParserFactory(Map<String,?>).createParser(JsonObject)
+   */
+  @Test
+  public void jsonParserTest2() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(JsonObject)]");
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "Create JsonObject from (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      JsonObject jsonObj = JSONP_Util
+          .createJsonObjectFromString(JSONP_Data.jsonObjectWithAllTypesOfData);
+      JSONP_Util.dumpJsonObject(jsonObj);
+      System.out.println("Create JsonParser from the JsonObject");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(jsonObj);
+      System.out.println("parser=" + parser);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      pass = parseAndVerify_JsonObjectWithAllTypesOfData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest2 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:133;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in "JSONP_Data.jsonObjectWithLotsOfNestedObjectsData".
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParser(Reader)
+   */
+  @Test
+  public void jsonParserTest3() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("-------------------------------------------");
+      System.out.println("TEST CASE [Json.createParser(Reader) again]");
+      System.out.println("-------------------------------------------");
+      System.out.println(
+          "Create Reader from (JSONP_Data.jsonObjectWithLotsOfNestedObjectsData)");
+      StringReader reader = new StringReader(
+          JSONP_Data.jsonObjectWithLotsOfNestedObjectsData);
+      System.out.println("Create JsonParser from the Reader");
+      parser = Json.createParser(reader);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonObjectWithLotsOfNestedObjectsData)");
+      pass = parseAndVerify_JsonObjectWithLotsOfNestedObjectsData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest3 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:417;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:166;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in "JSONP_Data.jsonObjectithLotsOfNestedObjectsData".
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser =
+   * Json.createParserFactory(Map<String,?>).createParser(JsonObject)
+   */
+  @Test
+  public void jsonParserTest4() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "-----------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(JsonObject object) again]");
+      System.out.println(
+          "-----------------------------------------------------------------------------------------");
+      System.out.println(
+          "Create JsonObject from (JSONP_Data.jsonObjectWithLotsOfNestedObjectsData)");
+      JsonObject jsonObj = JSONP_Util.createJsonObjectFromString(
+          JSONP_Data.jsonObjectWithLotsOfNestedObjectsData);
+      JSONP_Util.dumpJsonObject(jsonObj);
+      System.out.println("Create JsonParser from the JsonObject");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(jsonObj);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonObjectWithLotsOfNestedObjectsData)");
+      pass = parseAndVerify_JsonObjectWithLotsOfNestedObjectsData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest4 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:417; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:167;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonArray defined in "JSONP_Data.jsonArrayWithMultipleArraysData". Creates
+   * the JsonParser via the API:
+   *
+   * JsonParser parser =
+   * Json.createParserFactory(Map<String,?>).createParser(JsonArray)
+   */
+  @Test
+  public void jsonParserTest5() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "---------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(JsonArray)]");
+      System.out.println(
+          "---------------------------------------------------------------------------");
+      System.out.println(
+          "Create JsonArray from (JSONP_Data.jsonArrayWithMultipleArraysData)");
+      JsonArray jsonArr = JSONP_Util.createJsonArrayFromString(
+          JSONP_Data.jsonArrayWithMultipleArraysData);
+      JSONP_Util.dumpJsonArray(jsonArr);
+      System.out.println("Create JsonParser from the JsonArray");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(jsonArr);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonArrayWithMultipleArraysData)");
+      pass = parseAndVerify_JsonArrayWithMultipleArraysData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest5 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest6
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:172; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonArray defined in resource file "jsonArrayWithAllTypesOfData.json".
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParser(InputStream)
+   */
+  @Test
+  public void jsonParserTest6() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("------------------------------------------");
+      System.out.println("TEST CASE [Json.createParser(InputStream)]");
+      System.out.println("------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonArrayWithAllTypesOfData.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonArrayWithAllTypesOfData.json");
+      System.out.println("Create JsonParser from the InputStream");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonArrayWithAllTypesOfData.json)");
+      pass = parseAndVerify_JsonArrayWithAllTypesOfData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest6 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest6 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest7
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:417; JSONP:JAVADOC:164; JSONP:JAVADOC:235; JSONP:JAVADOC:237;
+   * JSONP:JAVADOC:239; JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser with a configuration. Verifies
+   * PARSING of the JsonObject defined in
+   * "JSONP_Data.jsonObjectWithAllTypesOfData". Creates the JsonParser via the
+   * following API
+   *
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(Reader)
+   */
+  @Test
+  public void jsonParserTest7() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "-------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(Reader)]");
+      System.out.println(
+          "-------------------------------------------------------------------------");
+      System.out.println("Create a Reader from (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      StringReader reader = new StringReader(
+          JSONP_Data.jsonObjectWithAllTypesOfData);
+      System.out.println("Create JsonParser using Reader and a configuration");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(reader);
+      System.out.println("Call JsonParser.toString() to print the JsonObject");
+      parser.toString();
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonObjectWithAllTypesOfData)");
+      if (!parseAndVerify_JsonObjectWithAllTypesOfData(parser))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest7 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest7 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest8
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:417;
+   * JSONP:JAVADOC:167; JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser with a configuration. Verifies
+   * PARSING of the JsonArray defined in
+   * "JSONP_Data.jsonArrayWithLotsOfNestedObjectsData". Creates the JsonParser
+   * via the following API
+   *
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(JsonArray)
+   */
+  @Test
+  public void jsonParserTest8() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(JsonArray)]");
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "Create a JsonArray from (JSONP_Data.jsonArrayWithLotsOfNestedObjectsData)");
+      JsonArray jsonArr = JSONP_Util.createJsonArrayFromString(
+          JSONP_Data.jsonArrayWithLotsOfNestedObjectsData);
+      JSONP_Util.dumpJsonArray(jsonArr);
+      System.out.println("Create JsonParser using JsonArray and a configuration");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(jsonArr);
+      System.out.println("Call JsonParser.toString() to print the JsonObject");
+      parser.toString();
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonArrayWithLotsOfNestedObjectsData)");
+      if (!parseAndVerify_JsonArrayWithLotsOfNestedObjectsData(parser))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest8 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest8 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest9
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:417; JSONP:JAVADOC:167; JSONP:JAVADOC:235; JSONP:JAVADOC:237;
+   * JSONP:JAVADOC:239; JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser with an empty configuration.
+   * Verifies PARSING of the JsonArray defined in
+   * "JSONP_Data.jsonArrayWithMultipleArraysData". Creates the JsonParser via
+   * the following API
+   * 
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(JsonArray)
+   */
+  @Test
+  public void jsonParserTest9() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(JsonArray)]");
+      System.out.println(
+          "----------------------------------------------------------------------------");
+      System.out.println(
+          "Create JsonArray from (JSONP_Data.jsonArrayWithMultipleArraysData)");
+      JsonArray jsonArr = JSONP_Util.createJsonArrayFromString(
+          JSONP_Data.jsonArrayWithMultipleArraysData);
+      JSONP_Util.dumpJsonArray(jsonArr);
+      System.out.println("Create JsonParser using JsonArray and a configuration");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(jsonArr);
+      System.out.println("Call JsonParser.toString() to print the JsonArray");
+      parser.toString();
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (JSONP_Data.jsonArrayWithMultipleArraysData)");
+      if (!parseAndVerify_JsonArrayWithMultipleArraysData(parser))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest9 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest9 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest10
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:417;
+   * JSONP:JAVADOC:165; JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in resource file
+   * "jsonObjectWithLotsOfNestedObjectsData.json". Creates the JsonParser via
+   * the following API
+   * 
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(InputStream)
+   */
+  @Test
+  public void jsonParserTest10() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(InputStream)]");
+      System.out.println(
+          "------------------------------------------------------------------------------");
+      System.out.println("Create JsonParser using InputStream and a configuration");
+      InputStream istream = JSONP_Util.getInputStreamFromResource(
+          "jsonObjectWithLotsOfNestedObjectsData.json");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream);
+      System.out.println("Call JsonParser.toString() to print the JsonObject");
+      parser.toString();
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectWithLotsOfNestedObjectsData.json)");
+      if (!parseAndVerify_JsonObjectWithLotsOfNestedObjectsData(parser))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest10 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest10 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest11
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239; JSONP:JAVADOC:375;
+   * JSONP:JAVADOC:376; JSONP:JAVADOC:417; JSONP:JAVADOC:201;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonArray defined in resource file
+   * "jsonArrayWithAllTypesOfDataUTF16BE.json". Use UTF-16BE encoding.
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser =
+   * Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset)
+   */
+  @Test
+  public void jsonParserTest11() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset)]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonArrayWithAllTypesOfDataUTF16BE.json)");
+      InputStream istream = JSONP_Util.getInputStreamFromResource(
+          "jsonArrayWithAllTypesOfDataUTF16BE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-16BE");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_16BE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonArrayWithAllTypesOfDataUTF16BE.json)");
+      pass = parseAndVerify_JsonArrayWithAllTypesOfData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest11 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest11 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest12
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:417;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:201;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonArray defined in resource file
+   * "jsonArrayWithLotsOfNestedArraysData.json". Use UTF-8 encoding.
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(InputStream, Charset)
+   */
+  @Test
+  public void jsonParserTest12() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "---------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(InputStream, Charset)]");
+      System.out.println(
+          "---------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonArrayWithLotsOfNestedArraysData.json)");
+      InputStream istream = JSONP_Util.getInputStreamFromResource(
+          "jsonArrayWithLotsOfNestedArraysData.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-8 and a configuration");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_8);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonArrayWithLotsOfNestedArraysData.json)");
+      if (!parseAndVerify_JsonArrayWithLotsOfNestedArraysData(parser))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest12 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest12 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest13
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:201; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   *
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in UTF-16LE encoding resource file
+   * "jsonObjectWithAllTypesOfDataUTF16LE.json".
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParserFactory(Map<String,
+   * ?>).createParser(InputStream, Charset)
+   */
+  @Test
+  public void jsonParserTest13() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "---------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String, ?>).createParser(InputStream, Charset)]");
+      System.out.println(
+          "---------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectWithAllTypesOfDataUTF16LE.json)");
+      InputStream istream = JSONP_Util.getInputStreamFromResource(
+          "jsonObjectWithAllTypesOfDataUTF16LE.json");
+      System.out.println("Create JsonParser from the InputStream using UTF-16LE encoding");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_16LE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectWithAllTypesOfDataUTF16LE.json)");
+      pass = parseAndVerify_JsonObjectWithAllTypesOfData(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest13 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest13 Failed");
+  }
+
+  /*
+   * @testName: jsonParserTest14
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:172;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:477;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in resource file "jsonHelloWorld.json.json".
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParser(InputStream)
+   */
+  @Test
+  public void jsonParserTest14() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println("------------------------------------------");
+      System.out.println("TEST CASE [Json.createParser(InputStream)]");
+      System.out.println("------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonHelloWorld.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonHelloWorld.json");
+      System.out.println("Create JsonParser from the InputStream");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonHelloWorld.json)");
+      pass = parseAndVerify_JsonHelloWorld(parser);
+    } catch (Exception e) {
+      throw new Fault("jsonParserTest14 Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("jsonParserTest14 Failed");
+  }
+
+  /*
+   * @testName: parseUTFEncodedTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:122; JSONP:JAVADOC:417;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376; JSONP:JAVADOC:201;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in resource files:
+   *
+   * jsonObjectEncodingUTF8.json jsonObjectEncodingUTF16.json
+   * jsonObjectEncodingUTF16LE.json jsonObjectEncodingUTF16BE.json
+   * jsonObjectEncodingUTF32LE.json jsonObjectEncodingUTF32BE.json
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser =
+   * Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset)
+   *
+   * For each supported encoding supported by JSON RFC parse the JsonObject and
+   * verify we get the expected results. The Charset encoding is passed in as an
+   * argument for each encoding type tested.
+   */
+  @Test
+  public void parseUTFEncodedTests() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-8]");
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF8.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF8.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-8");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_8);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF8.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-8 encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-16]");
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-16");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_16);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF16.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-16 encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-16LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16LE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16LE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-16LE");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_16LE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF16LE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-16LE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-16BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16BE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16BE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-16BE");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_16BE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF16BE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-16BE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-32LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32LE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32LE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-32LE");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_32LE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF32LE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-32LE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "-------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParserFactory(Map<String,?>).createParser(InputStream, Charset) as UTF-32BE]");
+      System.out.println(
+          "-------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32BE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32BE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream with character encoding UTF-32BE");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(istream, JSONP_Util.UTF_32BE);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF32BE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-32BE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("parseUTFEncodedTests Failed");
+  }
+
+  /*
+   * @testName: parseUTFEncodedTests2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:117; JSONP:JAVADOC:120; JSONP:JAVADOC:122;
+   * JSONP:JAVADOC:172; JSONP:JAVADOC:235; JSONP:JAVADOC:237; JSONP:JAVADOC:239;
+   * JSONP:JAVADOC:375; JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Tests the JsonParser parser. Verifies PARSING of the
+   * JsonObject defined in resource files and auto-detecting the encoding:
+   *
+   * jsonObjectEncodingUTF8.json jsonObjectEncodingUTF16LE.json
+   * jsonObjectEncodingUTF16BE.json jsonObjectEncodingUTF32LE.json
+   * jsonObjectEncodingUTF32BE.json
+   * 
+   * Creates the JsonParser via the API:
+   *
+   * JsonParser parser = Json.createParser(InputStream)
+   *
+   * For each supported encoding supported by JSON RFC the above should
+   * auto-detect the encoding and verify we get the expected results.
+   */
+  @Test
+  public void parseUTFEncodedTests2() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    JsonParser.Event event = null;
+    try {
+      System.out.println(
+          "-------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParser(InputStream) and auto-detect as UTF-8]");
+      System.out.println(
+          "-------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF8.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF8.json");
+      System.out.println(
+          "Create JsonParser from the InputStream and auto-detect character encoding UTF-8");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF8.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-8 encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParser(InputStream) and auto-detect as UTF-16LE]");
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16LE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16LE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream and auto-detect character encoding UTF-16LE");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF16LE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-16LE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParser(InputStream) and auto-detect as UTF-16BE]");
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16BE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16BE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream and auto-detect character encoding UTF-16BE");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF16BE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-16BE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParser(InputStream) and auto-detect as UTF-32LE]");
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32LE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32LE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream and auto-detect character encoding UTF-32LE");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF32LE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-32LE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createParser(InputStream) and auto-detect as UTF-32BE]");
+      System.out.println(
+          "----------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32BE.json)");
+      InputStream istream = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32BE.json");
+      System.out.println(
+          "Create JsonParser from the InputStream and auto-detect character encoding UTF-32BE");
+      parser = Json.createParser(istream);
+      System.out.println(
+          "Verify that JSON Parser Events/Data matches (jsonObjectEncodingUTF32BE.json)");
+      if (!parseAndVerify_JsonObjectUTF(parser))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing parsing of UTF-32BE encoding: " + e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+
+    if (!pass)
+      throw new Fault("parseUTFEncodedTests2 Failed");
+  }
+
+  /*
+   * @testName: jsonParserIsIntegralNumberTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:120; JSONP:JAVADOC:133; JSONP:JAVADOC:375;
+   * JSONP:JAVADOC:376;
+   * 
+   * @test_Strategy: Test JsonParser.isIntegralNumber() method.
+   */
+  @Test
+  public void jsonParserIsIntegralNumberTest() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    String jsonTestString = "[123, 12345.45]";
+    try {
+      System.out.println("Create JsonParser");
+      parser = Json.createParser(new StringReader(jsonTestString));
+      // INTEGRAL NUMBER TEST
+      JsonParser.Event event = JSONP_Util.getNextSpecificParserEvent(parser,
+          JsonParser.Event.VALUE_NUMBER); // e=JsonParser.Event.VALUE_NUMBER
+      JSONP_Util.dumpEventType(event);
+      if (!JSONP_Util.assertEqualsJsonNumberType(parser.isIntegralNumber(),
+          JSONP_Util.INTEGRAL))
+        pass = false;
+      else {
+        if (!JSONP_Util.assertEquals(123, parser.getInt()))
+          pass = false;
+      }
+      // NON_INTEGRAL NUMBER TEST
+      event = JSONP_Util.getNextSpecificParserEvent(parser,
+          JsonParser.Event.VALUE_NUMBER); // e=JsonParser.Event.VALUE_NUMBER
+      JSONP_Util.dumpEventType(event);
+      if (!JSONP_Util.assertEqualsJsonNumberType(parser.isIntegralNumber(),
+          JSONP_Util.NON_INTEGRAL))
+        pass = false;
+      else {
+        if (!JSONP_Util.assertEquals(12345.45,
+            parser.getBigDecimal().doubleValue()))
+          pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonParserIsIntegralNumberTest Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+
+    if (!pass)
+      throw new Fault("jsonParserIsIntegralNumberTest Failed");
+  }
+
+  private boolean tripIllegalStateException(JsonParser parser,
+      JsonParser.Event event) {
+    boolean pass = true;
+
+    // Check in case event is null
+    if (event == null) {
+      System.err.println("event is null - unexpected.");
+      return false;
+    }
+    System.out.println("Event=" + JSONP_Util.getEventTypeString(event));
+    System.out.println("Testing call to JsonParser.getString()");
+    if (event != JsonParser.Event.VALUE_STRING
+        && event != JsonParser.Event.VALUE_NUMBER
+        && event != JsonParser.Event.KEY_NAME) {
+      try {
+        System.out.println("Trip IllegalStateException by calling JsonParser.getString()");
+        String string = parser.getString();
+        pass = false;
+        System.err.println("Failed to throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("Got expected IllegalStateException");
+      } catch (Exception e) {
+        pass = false;
+        System.err.println("Caught unexpected exception: " + e);
+      }
+    } else {
+      System.out.println("No testing for IllegalStateException for this scenario.");
+    }
+
+    System.out.println("Testing call to JsonParser.isIntegralNumber()");
+    if (event != JsonParser.Event.VALUE_NUMBER) {
+      try {
+        System.out.println(
+            "Trip IllegalStateException by calling JsonParser.isIntegralNumber()");
+        boolean numberType = parser.isIntegralNumber();
+        pass = false;
+        System.err.println("Failed to throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("Got expected IllegalStateException");
+      } catch (Exception e) {
+        pass = false;
+        System.err.println("Caught unexpected exception: " + e);
+      }
+    } else {
+      System.out.println("No testing for IllegalStateException for this scenario.");
+    }
+
+    System.out.println("Testing call to JsonParser.getBigDecimal()");
+    if (event != JsonParser.Event.VALUE_NUMBER) {
+      try {
+        System.out.println(
+            "Trip IllegalStateException by calling JsonParser.getBigDecimal()");
+        BigDecimal number = parser.getBigDecimal();
+        pass = false;
+        System.err.println("Failed to throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("Got expected IllegalStateException");
+      } catch (Exception e) {
+        pass = false;
+        System.err.println("Caught unexpected exception: " + e);
+      }
+    } else {
+      System.out.println("No testing for IllegalStateException for this scenario.");
+    }
+
+    System.out.println("Testing call to JsonParser.getInt()");
+    if (event != JsonParser.Event.VALUE_NUMBER) {
+      try {
+        System.out.println("Trip IllegalStateException by calling JsonParser.getInt()");
+        int number = parser.getInt();
+        pass = false;
+        System.err.println("Failed to throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("Got expected IllegalStateException");
+      } catch (Exception e) {
+        pass = false;
+        System.err.println("Caught unexpected exception: " + e);
+      }
+    } else {
+      System.out.println("No testing for IllegalStateException for this scenario.");
+    }
+
+    System.out.println("Testing call to JsonParser.getLong()");
+    if (event != JsonParser.Event.VALUE_NUMBER) {
+      try {
+        System.out.println("Trip IllegalStateException by calling JsonParser.getLong()");
+        long number = parser.getLong();
+        pass = false;
+        System.err.println("Failed to throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("Got expected IllegalStateException");
+      } catch (Exception e) {
+        pass = false;
+        System.err.println("Caught unexpected exception: " + e);
+      }
+    } else {
+      System.out.println("No testing for IllegalStateException for this scenario.");
+    }
+    return pass;
+  }
+
+  /*
+   * @testName: jsonParserIllegalExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:121; JSONP:JAVADOC:123; JSONP:JAVADOC:236;
+   * JSONP:JAVADOC:238; JSONP:JAVADOC:240;
+   * 
+   * @test_Strategy: Test JsonParser exception conditions. Trip the following
+   * exceptions:
+   *
+   * java.lang.IllegalStateException
+   */
+  @Test
+  public void jsonParserIllegalExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonParser parser = null;
+    String jsonTestString = "[\"string\",100,false,null,true,{\"foo\":\"bar\"}]";
+    try {
+      System.out.println("Create JsonParser");
+      parser = Json.createParserFactory(JSONP_Util.getEmptyConfig())
+          .createParser(new StringReader(jsonTestString));
+      JsonParser.Event event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.START_ARRAY */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_STRING */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_NUMBER */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_FALSE */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_NULL */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_TRUE */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.START_OBJECT */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.KEY_NAME */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.VALUE_STRING */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.END_OBJECT */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+      event = JSONP_Util
+          .getNextParserEvent(parser); /* e=JsonParser.Event.END_ARRAY */
+      if (!tripIllegalStateException(parser, event))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonParserIllegalExceptionTests Failed: ", e);
+    } finally {
+      try {
+        parser.close();
+      } catch (Exception e) {
+      }
+    }
+
+    if (!pass)
+      throw new Fault("jsonParserIllegalExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonParserIOErrorTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:207; JSONP:JAVADOC:389; JSONP:JAVADOC:415;
+   * 
+   * @test_Strategy: Tests for JsonException for testable i/o errors.
+   *
+   */
+  @SuppressWarnings("ConvertToTryWithResources")
+  @Test
+  public void jsonParserIOErrorTests() throws Fault {
+    boolean pass = true;
+
+    String jsonText = "{\"name1\":\"value1\",\"name2\":\"value2\"}";
+
+    // Trip JsonException if there is an i/o error on
+    // Json.createParser(InputStream)
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on Json.createParser(InputStream).");
+      System.out.println("Parsing " + jsonText);
+      InputStream is = JSONP_Util.getInputStreamFromString(jsonText);
+      MyBufferedInputStream mbi = new MyBufferedInputStream(is, true);
+      System.out.println("Calling Json.createParser(InputStream)");
+      JsonParser parser = Json.createParser(mbi);
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on JsonParser.next()
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonParser.next().");
+      System.out.println("Parsing " + jsonText);
+      InputStream is = JSONP_Util.getInputStreamFromString(jsonText);
+      MyBufferedInputStream mbi = new MyBufferedInputStream(is, true);
+      JsonParser parser = Json.createParser(mbi);
+      System.out.println("Calling JsonParser.next()");
+      parser.next();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on JsonParser.close()
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonParser.close().");
+      System.out.println("Parsing " + jsonText);
+      InputStream is = JSONP_Util.getInputStreamFromString(jsonText);
+      MyBufferedInputStream mbi = new MyBufferedInputStream(is);
+      JsonParser parser = Json.createParser(mbi);
+      mbi.setThrowIOException(true);
+      System.out.println("Calling JsonParser.close()");
+      parser.close();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonParserIOErrorTests Failed");
+  }
+
+  /*
+   * @testName: jsonParserExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:390; JSONP:JAVADOC:391;
+   * 
+   * @test_Strategy: Tests for the following exception test cases:
+   *
+   * JsonParsingException - if incorrect JSON is encountered while advancing
+   * parser to next state NoSuchElementException - if there are no more parsing
+   * states
+   *
+   */
+  @Test
+  public void jsonParserExceptionTests() throws Fault {
+    boolean pass = true;
+
+    // Trip JsonParsingException for JsonParser.next() if incorrect JSON is
+    // encountered
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonParser.next() if incorrect JSON is encountered");
+      InputStream is = JSONP_Util.getInputStreamFromString("}{");
+      JsonParser parser = Json.createParser(is);
+      parser.next();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip NoSuchElementException for JsonParser.next() if no more parsing
+    // states
+    try {
+      System.out.println(
+          "Trip NoSuchElementException for JsonParser.next() if no more parsing states");
+      InputStream is = JSONP_Util.getInputStreamFromString("{}");
+      JsonParser parser = Json.createParser(is);
+      parser.next(); // Event -> START_OBJECT {
+      parser.next(); // Event -> END_OBJECT }
+      parser.next(); // Event -> NoSuchElementException should be thrown
+      System.err.println("Did not get expected NoSuchElementException");
+      pass = false;
+    } catch (NoSuchElementException e) {
+      System.out.println("Caught expected NoSuchElementException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonParserExceptionTests Failed");
+  }
+
+  /*
+   * @testName: invalidLiteralNamesTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:133; JSONP:JAVADOC:390;
+   * 
+   * @test_Strategy: This test trips various JsonParsingException conditions
+   * when parsing an uppercase literal name that must be lowercase per JSON RFC
+   * for the literal values (true, false or null).
+   *
+   */
+  @Test
+  public void invalidLiteralNamesTest() throws Fault {
+    boolean pass = true;
+
+    // Trip JsonParsingException for JsonParser.next() if invalid liternal TRUE
+    // instead of true
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonParser.next() if invalid liternal TRUE instead of true.");
+      System.out.println("Reading " + "[TRUE]");
+      JsonParser parser = Json.createParser(new StringReader("[TRUE]"));
+      parser.next(); // Event -> START_OBJECT {
+      parser.next(); // Event -> JsonParsingException (invalid literal TRUE)
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonParser.next() if invalid liternal FALSE
+    // instead of false
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonParser.next() if invalid liternal FALSE instead of false.");
+      System.out.println("Reading " + "[FALSE]");
+      JsonParser parser = Json.createParser(new StringReader("[FALSE]"));
+      parser.next(); // Event -> START_OBJECT {
+      parser.next(); // Event -> JsonParsingException (invalid literal FALSE)
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonParser.next() if invalid liternal NULL
+    // instead of null
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonParser.next() if invalid liternal NULL instead of null.");
+      System.out.println("Reading " + "[NULL]");
+      JsonParser parser = Json.createParser(new StringReader("[NULL]"));
+      parser.next(); // Event -> START_OBJECT {
+      parser.next(); // Event -> JsonParsingException (invalid literal NULL)
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("invalidLiteralNamesTest Failed");
+  }
+
+  /*
+   * @testName: jsonParser11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:673; JSONP:JAVADOC:674; JSONP:JAVADOC:675;
+   * JSONP:JAVADOC:676; JSONP:JAVADOC:677; JSONP:JAVADOC:678; JSONP:JAVADOC:679;
+   * JSONP:JAVADOC:680; JSONP:JAVADOC:583; JSONP:JAVADOC:584; JSONP:JAVADOC:585;
+   * JSONP:JAVADOC:586; JSONP:JAVADOC:587; JSONP:JAVADOC:588; JSONP:JAVADOC:662;
+   * JSONP:JAVADOC:663; JSONP:JAVADOC:664; JSONP:JAVADOC:665; JSONP:JAVADOC:666;
+   * JSONP:JAVADOC:667;
+   * 
+   * @test_Strategy: Tests JsonParser API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonParser11Test() throws Fault {
+    Parser parserTest = new Parser();
+    final TestResult result = parserTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/Parser.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/Parser.java
new file mode 100644
index 0000000..3e5e988
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonparsertests/Parser.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonparsertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonParser;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests: {@link JsonParser} API
+ * methods added in JSON-P 1.1.
+ */
+public class Parser {
+
+  /** Tests input data with various JsonValue instances. */
+  private static final JsonValue[] VALUES = new JsonValue[] {
+      toJsonValue(STR_VALUE), // Non JsonObject with String
+      toJsonValue(INT_VALUE), // Non JsonObject with int
+      toJsonValue(LNG_VALUE), // Non JsonObject with long
+      toJsonValue(DBL_VALUE), // Non JsonObject with double
+      toJsonValue(BOOL_VALUE), // Non JsonObject with boolean
+      toJsonValue(BDC_VALUE), // Non JsonObject with BigDecimal
+      toJsonValue(BIN_VALUE), // Non JsonObject with BigInteger
+      createSimpleObjectStr(), // JsonObject with String
+      createSimpleObjectInt(), // JsonObject with int
+      createSimpleObjectBool(), // JsonObject with boolean
+      createSimpleObjectObject(), // JsonObject with JsonObject
+      createEmptyArrayWithStr(), // JsonArray with String
+      createEmptyArrayWithInt(), // JsonArray with int
+      createEmptyArrayWithBool(), // JsonArray with boolean
+      createEmptyArrayWithObject() // JsonArray with JsonObject
+  };
+
+  // /** Tests input data with simple JsonValue instances. */
+  // private static final JsonValue[] SIMPLE_VALUES = new JsonValue[] {
+  // toJsonValue(STR_VALUE), // Non JsonObject with String
+  // toJsonValue(INT_VALUE), // Non JsonObject with int
+  // toJsonValue(LNG_VALUE), // Non JsonObject with long
+  // toJsonValue(BOOL_VALUE), // Non JsonObject with boolean
+  // toJsonValue(BDC_VALUE), // Non JsonObject with BigDecimal
+  // toJsonValue(BIN_VALUE) // Non JsonObject with BigInteger
+  // };
+
+  /** Tests input data with compound JsonValue instances (object or array). */
+  private static final JsonStructure[] COMPOUND_VALUES = new JsonStructure[] {
+      createSimpleObjectStr(), // JsonObject with String
+      createSimpleObjectInt(), // JsonObject with int
+      createSimpleObjectBool(), // JsonObject with boolean
+      createSimpleObjectObject(), // JsonObject with JsonObject
+      createEmptyArrayWithStr(), // JsonArray with String
+      createEmptyArrayWithInt(), // JsonArray with int
+      createEmptyArrayWithBool(), // JsonArray with boolean
+      createEmptyArrayWithObject() // JsonArray with JsonObject
+  };
+
+  /** Tests input data with empty JsonObject and JsonArray instances. */
+  private static final JsonStructure[] EMPTY_VALUES = new JsonStructure[] {
+      createEmptyObject(), // Empty JsonObject
+      createEmptyArray() // Empty JsonArray
+  };
+
+  /** Tests input data with JsonObject instances. */
+  private static final JsonObject[] OBJ_VALUES = new JsonObject[] {
+      createSimpleObjectStr(), // JsonObject with String
+      createSimpleObjectInt(), // JsonObject with int
+      createSimpleObjectBool(), // JsonObject with boolean
+      createSimpleObjectObject(), // JsonObject with JsonObject
+      createSimpleObjectWithStr() // JsonObject with default value (String) and
+                                  // another String
+
+  };
+
+  /** Tests input data with non JsonObject instances. */
+  private static final JsonValue[] NON_OBJ_VALUES = new JsonValue[] {
+      toJsonValue(STR_VALUE), // Non JsonObject with String
+      toJsonValue(INT_VALUE), // Non JsonObject with int
+      toJsonValue(LNG_VALUE), // Non JsonObject with long
+      toJsonValue(DBL_VALUE), // Non JsonObject with double
+      toJsonValue(BOOL_VALUE), // Non JsonObject with boolean
+      toJsonValue(BDC_VALUE), // Non JsonObject with BigDecimal
+      toJsonValue(BIN_VALUE), // Non JsonObject with BigInteger
+      createEmptyArrayWithStr(), // JsonArray with String
+      createEmptyArrayWithInt(), // JsonArray with int
+      createEmptyArrayWithBool(), // JsonArray with boolean
+      createEmptyArrayWithObject() // JsonArray with JsonObject
+  };
+
+  /** Tests input data with JsonArray instances. */
+  private static final JsonArray[] ARRAY_VALUES = new JsonArray[] {
+      createEmptyArrayWithStr(), // JsonArray with String
+      createEmptyArrayWithInt(), // JsonArray with int
+      createEmptyArrayWithBool(), // JsonArray with boolean
+      createEmptyArrayWithObject() // JsonArray with JsonObject
+  };
+
+  /** Tests input data with non JsonArray instances. */
+  private static final JsonValue[] NON_ARRAY_VALUES = new JsonValue[] {
+      toJsonValue(STR_VALUE), // Non JsonObject with String
+      toJsonValue(INT_VALUE), // Non JsonObject with int
+      toJsonValue(LNG_VALUE), // Non JsonObject with long
+      toJsonValue(DBL_VALUE), // Non JsonObject with double
+      toJsonValue(BOOL_VALUE), // Non JsonObject with boolean
+      toJsonValue(BDC_VALUE), // Non JsonObject with BigDecimal
+      toJsonValue(BIN_VALUE), // Non JsonObject with BigInteger
+      createSimpleObjectStr(), // JsonObject with String
+      createSimpleObjectInt(), // JsonObject with int
+      createSimpleObjectBool(), // JsonObject with boolean
+      createSimpleObjectObject() // JsonObject with JsonObject
+  };
+
+  /**
+   * Creates an instance of {@link JsonParser} API methods added in JSON-P 1.1
+   * test.
+   */
+  Parser() {
+    super();
+  }
+
+  /**
+   * Test {@link JsonParser} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonParser API methods added in JSON-P 1.1.");
+    System.out.println("JsonParser API methods added in JSON-P 1.1.");
+    testGetObject(result);
+    testGetNonObject(result);
+    testGetArray(result);
+    testGetNonArray(result);
+    testGetValue(result);
+    testGetIllegalValue(result);
+    testGetObjectStream(result);
+    testGetNonObjectStream(result);
+    testGetArrayStream(result);
+    testGetNonArrayStream(result);
+    testGetValueStream(result);
+    testGetCompoundValueStream(result);
+    testSkipArray(result);
+    testSkipNonArray(result);
+    testSkipObject(result);
+    testSkipNonObject(result);
+    return result;
+  }
+
+  /**
+   * Test {@code JsonParser getObject()} method on JSON object values.
+   */
+  private void testGetObject(final TestResult result) {
+    for (JsonObject value : OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getObject() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        JsonObject out = parser.getObject();
+        if (operationFailed(value, out)) {
+          result.fail("getObject()", "Output value " + valueToString(out)
+              + " shall be " + valueToString(value));
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("getObject()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getObject()} method on non JSON object values.
+   */
+  private void testGetNonObject(final TestResult result) {
+    for (JsonValue value : NON_OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getObject() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.getObject();
+        result.fail("getObject()",
+            "Calling method on non object value shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getObject()",
+            "Calling method on non object value shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getArray()} method on JSON array values.
+   */
+  private void testGetArray(final TestResult result) {
+    for (JsonArray value : ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getArray() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        JsonArray out = parser.getArray();
+        if (operationFailed(value, out)) {
+          result.fail("getArray()", "Output value " + valueToString(out)
+              + " shall be " + valueToString(value));
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("getArray()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getArray()} method on non JSON object values.
+   */
+  private void testGetNonArray(final TestResult result) {
+    for (JsonValue value : NON_ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getArray() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.getArray();
+        result.fail("getArray()",
+            "Calling method on non array value shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getArray()",
+            "Calling method on non array value shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getValue()} method on common JSON values.
+   */
+  private void testGetValue(final TestResult result) {
+    for (JsonValue value : VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getValue() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        JsonValue out = parser.getValue();
+        if (operationFailed(value, out)) {
+          result.fail("getValue()", "Output value " + valueToString(out)
+              + " shall be " + valueToString(value));
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("getValue()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getValue()} method on END_OBJECT and END_ARRAY
+   * lexical elements.
+   */
+  private void testGetIllegalValue(final TestResult result) {
+    for (JsonValue value : EMPTY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getValue() on 2nd lexical element of " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next(); // Skip to START ELEMENT
+        parser.next(); // Skip to END ELEMENT
+        parser.getValue();
+        result.fail("getValue()",
+            "Calling method on END_OBJECT and END_ARRAY lexical elements shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getValue()",
+            "Calling method on END_OBJECT and END_ARRAY lexical elements shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getObjectStream()} method on JSON object values.
+   */
+  private void testGetObjectStream(final TestResult result) {
+    for (JsonObject value : OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getObjectStream() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        Stream<Map.Entry<String, JsonValue>> out = parser.getObjectStream();
+        if (operationFailed(value, out)) {
+          result.fail("getObjectStream()",
+              "Output Stream shall contain " + valueToString(value));
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("getObjectStream()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getObjectStream()} method on non JSON object values.
+   */
+  private void testGetNonObjectStream(final TestResult result) {
+    for (JsonValue value : NON_OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getObjectStream() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.getObjectStream();
+        result.fail("getObjectStream()",
+            "Calling method on non object value shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getObjectStream()",
+            "Calling method on non object value shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getArrayStream()} method on JSON array values.
+   */
+  private void testGetArrayStream(final TestResult result) {
+    for (JsonArray value : ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getArrayStream() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        Stream<JsonValue> out = parser.getArrayStream();
+        if (operationFailed(value, out)) {
+          result.fail("getArrayStream()",
+              "Output Stream shall contain " + valueToString(value));
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("getArrayStream()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getArrayStream()} method on non JSON array values.
+   */
+  private void testGetNonArrayStream(final TestResult result) {
+    for (JsonValue value : NON_ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getArrayStream() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.getArrayStream();
+        result.fail("getArrayStream()",
+            "Calling method on non array value shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getArrayStream()",
+            "Calling method on non array value shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getValueStream()} method on simple JSON values in
+   * document root.
+   */
+  private void testGetValueStream(final TestResult result) {
+    for (final JsonValue value : VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getValueStream() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        final Stream<JsonValue> outStream = parser.getValueStream();
+        int count = 0;
+        for (final Iterator<JsonValue> i = outStream.iterator(); i.hasNext();) {
+          final JsonValue out = i.next();
+          if (operationFailed(value, out)) {
+            result.fail("getValueStream()", "Output Stream value "
+                + valueToString(out) + " shall be " + valueToString(value));
+          }
+          count++;
+        }
+        if (count != 1) {
+          System.out.println("     Output Stream contains "
+              + Integer.toString(count) + " values, not 1");
+          result.fail("getValueStream()",
+              "Output Stream does not contain exactly 1 JSON value");
+        }
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser getValueStream()} method inside compound JSON values
+   * in document root.
+   */
+  private void testGetCompoundValueStream(final TestResult result) {
+    for (final JsonValue value : COMPOUND_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - getValueStream() inside " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.getValueStream();
+        result.fail("getValueStream()",
+            "Calling method on non object value shall throw IllegalStateException");
+      } catch (IllegalStateException e) {
+        System.out.println("      Expected exception: " + e.getMessage());
+      } catch (Throwable t) {
+        result.fail("getValueStream()",
+            "Calling method on non object value shall throw IllegalStateException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser skipArray()} method inside JSON array values.
+   * Expected result: Parser shall advance to the end of the stream after
+   * skipping an array which is the only value in the stream.
+   */
+  private void testSkipArray(final TestResult result) {
+    for (final JsonArray value : ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - skipArray() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.skipArray();
+        if (parser.hasNext()) {
+          result.fail("skipArray()",
+              "Parser did not davance to the end of the array");
+        }
+      } catch (Throwable t) {
+        System.out.println(
+            "     " + t.getClass().getSimpleName() + ": " + t.getMessage());
+        result.fail("skipArray()",
+            t.getClass().getSimpleName() + ": " + t.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser skipArray()} method outside JSON array values.
+   * Expected result: Parser shall not advance anywhere when called outside an
+   * array. Whole value shall match after {@code skipArray()} call because
+   * nothing shall happen in it.
+   */
+  private void testSkipNonArray(final TestResult result) {
+    for (final JsonValue value : NON_ARRAY_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - skipArray() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.skipArray();
+        final JsonValue out = parser.getValue();
+        if (operationFailed(value, out)) {
+          result.fail("skipArray()",
+              "Output value " + valueToString(out) + " shall be "
+                  + valueToString(value) + " even after skipArray()");
+        }
+      } catch (Throwable t) {
+        System.out.println(
+            "     " + t.getClass().getSimpleName() + ": " + t.getMessage());
+        result.fail("skipArray()",
+            t.getClass().getSimpleName() + ": " + t.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser skipObject()} method inside JSON object values.
+   * Expected result: Parser shall advance to the end of the stream after
+   * skipping an object which is the only value in the stream.
+   */
+  private void testSkipObject(final TestResult result) {
+    for (final JsonObject value : OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - skipObject() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.skipObject();
+        if (parser.hasNext()) {
+          result.fail("skipObject()",
+              "Parser did not davance to the end of the object");
+        }
+      } catch (Throwable t) {
+        System.out.println(
+            "     " + t.getClass().getSimpleName() + ": " + t.getMessage());
+        result.fail("skipObject()",
+            t.getClass().getSimpleName() + ": " + t.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonParser skipObject()} method outside JSON object values.
+   * Expected result: Parser shall not advance anywhere when called outside an
+   * object. Whole value shall match after {@code skipObject()} call because
+   * nothing shall happen in it.
+   */
+  private void testSkipNonObject(final TestResult result) {
+    for (final JsonValue value : NON_OBJ_VALUES) {
+      final String data = jsonData(value);
+      System.out.println(" - skipObject() on " + data);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonParser parser = Json.createParser(strReader)) {
+        parser.next();
+        parser.skipObject();
+        final JsonValue out = parser.getValue();
+        if (operationFailed(value, out)) {
+          result.fail("skipObject()",
+              "Output value " + valueToString(out) + " shall be "
+                  + valueToString(value) + " even after skipObject()");
+        }
+      } catch (Throwable t) {
+        System.out.println(
+            "     " + t.getClass().getSimpleName() + ": " + t.getMessage());
+        result.fail("skipObject()",
+            t.getClass().getSimpleName() + ": " + t.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    System.out.println("     Checking " + valueToString(out));
+    return out == null || !assertEquals(check, out);
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected Stream content.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonObject check,
+      final Stream<Map.Entry<String, JsonValue>> out) {
+    // Operation failed for null.
+    if (out == null) {
+      System.out.println("     Output is null");
+      return true;
+    }
+    final Set<String> keys = new HashSet<>(check.size());
+    // Clone key Set
+    for (final String key : check.keySet()) {
+      keys.add(key);
+    }
+    for (final Iterator<Map.Entry<String, JsonValue>> i = out.iterator(); i
+        .hasNext();) {
+      final Map.Entry<String, JsonValue> item = i.next();
+      final JsonValue checkValue = check.get(item.getKey());
+      System.out.println("     Checking " + valueToString(item.getValue()));
+      // Operation failed if values with the same key are not equal.
+      if (!item.getValue().equals(checkValue)) {
+        System.out.println("       check: " + valueToString(checkValue)
+            + " stream: " + valueToString(checkValue));
+        return true;
+      }
+      keys.remove(item.getKey());
+    }
+    // Operation failed if some unmatched keys remain in the set.
+    return !keys.isEmpty();
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected Stream content.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonArray check,
+      final Stream<JsonValue> out) {
+    // Operation failed for null.
+    if (out == null) {
+      System.out.println("     Output is null");
+      return true;
+    }
+    final Iterator<JsonValue> ci = check.iterator();
+    final Iterator<JsonValue> oi = out.iterator();
+    // To exit cycle, at least one of iterators does not have next value.
+    while (ci.hasNext() && oi.hasNext()) {
+      final JsonValue checkValue = ci.next();
+      final JsonValue outValue = oi.next();
+      System.out.println("     Checking " + valueToString(outValue));
+      if (!checkValue.equals(outValue)) {
+        System.out.println("       check: " + valueToString(checkValue)
+            + " stream: " + valueToString(checkValue));
+        return true;
+      }
+    }
+    // Check still has values, something was missing in output.
+    if (ci.hasNext()) {
+      System.out.println("     Output contains less values than expected");
+      return true;
+    }
+    // Output still has values, it contains more than expected.
+    if (oi.hasNext()) {
+      System.out.println("     Output contains more values than expected");
+      return true;
+    }
+    return false;
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreaderfactorytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreaderfactorytests/ClientTests.java
new file mode 100644
index 0000000..ccb99bb
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreaderfactorytests/ClientTests.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonreaderfactorytests;
+
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ArrayList;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonReaderFactoryTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:419; JSONP:JAVADOC:449; JSONP:JAVADOC:185;
+   * JSONP:JAVADOC:459;
+   * 
+   * @test_Strategy: Tests the JsonReaderFactory API.
+   *
+   * JsonReaderFactory readerFactory = Json.createReaderFactory(Map<String, ?>);
+   * JsonReader reader1 = readerFactory.createReader(Reader) JsonReader reader2
+   * = readerFactory.createReader(Reader)
+   */
+  @Test
+  public void jsonReaderFactoryTest1() throws Fault {
+    boolean pass = true;
+    JsonReader reader1 = null;
+    JsonReader reader2 = null;
+    JsonObject jsonObject = null;
+    String jsonObjectText = "{\"foo\":\"bar\"}";
+    try {
+      System.out.println("Create JsonReaderFactory with Map<String, ?> with EMPTY config");
+      JsonReaderFactory readerFactory = Json
+          .createReaderFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = readerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+      System.out.println("--------------------------------------------------");
+      System.out.println("TEST CASE [JsonReaderFactory.createReader(Reader)]");
+      System.out.println("--------------------------------------------------");
+      System.out.println("Create 1st JsonReader using JsonReaderFactory");
+      reader1 = readerFactory.createReader(new StringReader(jsonObjectText));
+      if (reader1 == null) {
+        System.err.println("ReaderFactory failed to create reader1");
+        pass = false;
+      } else {
+        jsonObject = reader1.readObject();
+        reader1.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+      System.out.println("Create 2nd JsonReader using JsonReaderFactory");
+      reader2 = readerFactory.createReader(new StringReader(jsonObjectText));
+      if (reader2 == null) {
+        System.err.println("ReaderFactory failed to create reader2");
+        pass = false;
+      } else {
+        jsonObject = reader2.readObject();
+        reader2.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonReaderFactoryTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonReaderFactoryTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonReaderFactoryTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:420; JSONP:JAVADOC:449; JSONP:JAVADOC:185;
+   * JSONP:JAVADOC:459;
+   * 
+   * @test_Strategy: Tests the JsonReaderFactory API.
+   *
+   * JsonReaderFactory readerFactory = Json.createReaderFactory(Map<String,?>);
+   * JsonReader reader1 = readerFactory.createReader(InputStream, Charset)
+   * JsonReader reader2 = readerFactory.createReader(InputStream, Charset)
+   *
+   * Create reader with both UTF-8 and UTF-16BE.
+   */
+  @Test
+  public void jsonReaderFactoryTest2() throws Fault {
+    boolean pass = true;
+    JsonReader reader1 = null;
+    JsonReader reader2 = null;
+    JsonObject jsonObject = null;
+    String jsonObjectText = "{\"foo\":\"bar\"}";
+    try {
+      System.out.println("Create JsonReaderFactory with Map<String, ?> with EMPTY config");
+      JsonReaderFactory readerFactory = Json
+          .createReaderFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = readerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println(
+          "----------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [JsonReaderFactory.createReader(InputStream, Charset)]");
+      System.out.println(
+          "----------------------------------------------------------------");
+      System.out.println(
+          "Create 1st JsonReader using JsonReaderFactory with UTF-8 encoding");
+      InputStream is1 = JSONP_Util.getInputStreamFromString(jsonObjectText);
+      reader1 = readerFactory.createReader(is1, JSONP_Util.UTF_8);
+      if (reader1 == null) {
+        System.err.println("ReaderFactory failed to create reader1");
+        pass = false;
+      } else {
+        jsonObject = reader1.readObject();
+        reader1.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+      System.out.println(
+          "Create 2nd JsonReader using JsonReaderFactory with UTF-8 encoding");
+      InputStream is2 = JSONP_Util.getInputStreamFromString(jsonObjectText);
+      reader2 = readerFactory.createReader(is2, JSONP_Util.UTF_8);
+      if (reader2 == null) {
+        System.err.println("ReaderFactory failed to create reader2");
+        pass = false;
+      } else {
+        jsonObject = reader2.readObject();
+        reader2.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonReaderFactoryTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonReaderFactoryTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonReaderFactoryTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:429; JSONP:JAVADOC:449; JSONP:JAVADOC:185;
+   * JSONP:JAVADOC:459;
+   * 
+   * @test_Strategy: Tests the JsonReaderFactory API.
+   *
+   * JsonReaderFactory readerFactory = Json.createReaderFactory(Map<String, ?>);
+   * JsonReader reader1 = readerFactory.createReader(InputStream) JsonReader
+   * reader2 = readerFactory.createReader(InputStream)
+   */
+  @Test
+  public void jsonReaderFactoryTest3() throws Fault {
+    boolean pass = true;
+    JsonReader reader1 = null;
+    JsonReader reader2 = null;
+    JsonObject jsonObject = null;
+    String jsonObjectText = "{\"foo\":\"bar\"}";
+    try {
+      System.out.println("Create JsonReaderFactory with Map<String, ?> with EMPTY config");
+      JsonReaderFactory readerFactory = Json
+          .createReaderFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = readerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-------------------------------------------------------");
+      System.out.println("TEST CASE [JsonReaderFactory.createReader(InputStream)]");
+      System.out.println("-------------------------------------------------------");
+      System.out.println("Create 1st JsonReader using JsonReaderFactory");
+      InputStream is1 = JSONP_Util.getInputStreamFromString(jsonObjectText);
+      reader1 = readerFactory.createReader(is1);
+      if (reader1 == null) {
+        System.err.println("ReaderFactory failed to create reader1");
+        pass = false;
+      } else {
+        jsonObject = reader1.readObject();
+        reader1.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+      System.out.println("Create 2nd JsonReader using JsonReaderFactory");
+      InputStream is2 = JSONP_Util.getInputStreamFromString(jsonObjectText);
+      reader2 = readerFactory.createReader(is2);
+      if (reader2 == null) {
+        System.err.println("ReaderFactory failed to create reader2");
+        pass = false;
+      } else {
+        jsonObject = reader2.readObject();
+        reader2.close();
+
+        if (!JSONP_Util.assertEquals(jsonObject.size(), 1)
+            || !JSONP_Util.assertEquals(jsonObject.getString("foo"), "bar"))
+          pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonReaderFactoryTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonReaderFactoryTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonReaderFactoryTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:449; JSONP:JAVADOC:459;
+   * 
+   * @test_Strategy: Tests the JsonReaderFactory API.
+   *
+   * JsonReaderFactory readerFactory = Json.createReaderFactory(Map<String, ?>);
+   * Map<String, ?> config = JsonReaderFactory.getConfigInUse();
+   *
+   * Test for the following 3 scenarios: 1) no supported provider property
+   * (empty config) 2) non supported provider property
+   */
+  @Test
+  public void jsonReaderFactoryTest4() throws Fault {
+    boolean pass = true;
+    JsonReaderFactory readerFactory;
+    Map<String, ?> config;
+    try {
+      System.out.println("----------------------------------------------");
+      System.out.println("Test scenario1: no supported provider property");
+      System.out.println("----------------------------------------------");
+      System.out.println("Create JsonReaderFactory with Map<String, ?> with EMPTY config");
+      readerFactory = Json.createReaderFactory(JSONP_Util.getEmptyConfig());
+      config = readerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-----------------------------------------------");
+      System.out.println("Test scenario2: non supported provider property");
+      System.out.println("-----------------------------------------------");
+      System.out.println("Create JsonReaderFactory with Map<String, ?> with FOO config");
+      readerFactory = Json.createReaderFactory(JSONP_Util.getFooConfig());
+      config = readerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonReaderFactoryTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonReaderFactoryTest4 Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/ClientTests.java
new file mode 100644
index 0000000..8a1b988
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/ClientTests.java
@@ -0,0 +1,3232 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonreadertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+// $Id$
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Utility Methods */
+
+  /*
+   * compareJsonObjectForUTFEncodedTests
+   */
+  private boolean compareJsonObjectForUTFEncodedTests(JsonObject jsonObject) {
+    boolean pass = true;
+    System.out.println("Comparing JsonObject values to expected results.");
+    String expString = "stringValue";
+    String actString = jsonObject.getJsonString("stringName").getString();
+    if (!JSONP_Util.assertEquals(expString, actString))
+      pass = false;
+    JsonObject actObject = jsonObject.getJsonObject("objectName");
+    expString = "bar";
+    actString = actObject.getJsonString("foo").getString();
+    if (!JSONP_Util.assertEquals(expString, actString))
+      pass = false;
+    JsonArray actArray = jsonObject.getJsonArray("arrayName");
+    if (!JSONP_Util.assertEquals(1, actArray.getJsonNumber(0).intValue())
+        || !JSONP_Util.assertEquals(2, actArray.getJsonNumber(1).intValue())
+        || !JSONP_Util.assertEquals(3, actArray.getJsonNumber(2).intValue()))
+      pass = false;
+    return pass;
+  }
+
+  /* Tests */
+
+  /*
+   * @testName: readEmptyArrayTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of an empty array "[]" from stream. Use
+   * JsonReader.readArray() API call.
+   *
+   */
+  @Test
+  public void readEmptyArrayTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String expJsonText = "[]";
+      System.out.println("Testing read of " + expJsonText);
+      reader = Json.createReader(new StringReader(expJsonText));
+      JsonArray array = reader.readArray();
+      pass = JSONP_Util.assertEqualsEmptyArrayList(array);
+    } catch (Exception e) {
+      throw new Fault("readEmptyArrayTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEmptyArrayTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeCharsInArrayTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of an array from a resource file with special
+   * chars in data. Use JsonReader.readArray() API call. Test scenario: Read
+   * string of JSON text containing a JSON array from resource file with
+   * following data: [ "popeye\"\\\/\b\f\n\r\tolive" ]
+   *
+   * These characters are backslash escape'd as follows: \" \\ \/ \b \f \n \r \t
+   *
+   * Create a JsonWriter to write above JsonArray to a string of JSON text.
+   * Re-read JsonWriter text back into a JsonArray Compare expected JSON array
+   * with actual JSON array for equality.
+   *
+   */
+  @Test
+  public void readEscapeCharsInArrayTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonArrayWithEscapeCharsData.json";
+    String expString = "popeye" + JSONP_Data.escapeCharsAsString + "olive";
+    try {
+
+      System.out.println("Reading contents of resource file " + resourceFile);
+      String readerContents = JSONP_Util
+          .getContentsOfResourceAsString(resourceFile);
+      System.out.println("readerContents=" + readerContents);
+
+      System.out.println("Testing read of resource contents: " + readerContents);
+      reader = Json.createReader(new StringReader(readerContents));
+      JsonArray expJsonArray = reader.readArray();
+
+      System.out.println("Dump of expJsonArray");
+      JSONP_Util.dumpJsonArray(expJsonArray);
+
+      System.out.println("Comparing JsonArray values with expected results.");
+      String actString = expJsonArray.getJsonString(0).getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+
+      System.out.println("Write the JsonArray 'expJsonArray' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(expJsonArray);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Create actJsonArray from read of writer contents: "
+          + writerContents);
+      reader = Json.createReader(new StringReader(writerContents));
+      JsonArray actJsonArray = reader.readArray();
+
+      System.out.println("Dump of actJsonArray");
+      JSONP_Util.dumpJsonArray(actJsonArray);
+
+      System.out.println("Compare expJsonArray and actJsonArray for equality");
+      if (!JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readEscapeCharsInArrayTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeCharsInArrayTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeUnicodeCharsInArrayTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of an array with unicode chars escaped and not
+   * escaped. Use JsonReader.readArray() API call. Test scenario: Read string of
+   * JSON text containing a JSON array with the following data: [
+   * "\\u0000\u00ff\\uff00\uffff" ]
+   *
+   * Notice unicode \u0000 and \uff00 is escaped but \u00ff and \uffff is not.
+   *
+   * Compare expected JSON String with actual JSON String for equality.
+   *
+   */
+  @Test
+  public void readEscapeUnicodeCharsInArrayTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String unicodeTextString = "[\"\\u0000\u00ff\\uff00\uffff\"]";
+    String expResult = "\u0000\u00ff\uff00\uffff";
+    try {
+      System.out.println("Reading array of escaped and non escaped unicode chars.");
+      reader = Json.createReader(new StringReader(unicodeTextString));
+      JsonArray array = reader.readArray();
+      String actResult = array.getJsonString(0).getString();
+      pass = JSONP_Util.assertEquals(expResult, actResult);
+    } catch (Exception e) {
+      throw new Fault("readEscapeUnicodeCharsInArrayTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeUnicodeCharsInArrayTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeUnicodeControlCharsInArrayTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of an array with unicode control chars escaped.
+   * Use JsonReader.readArray() API call. Test scenario: Read string of JSON
+   * text containing unicode control chars escaped as a Json Array.
+   *
+   * Compare expected JSON String with actual JSON String for equality.
+   *
+   */
+  @Test
+  public void readEscapeUnicodeControlCharsInArrayTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String unicodeTextString = "[\"" + JSONP_Data.unicodeControlCharsEscaped
+        + "\"]";
+    String expResult = JSONP_Data.unicodeControlCharsNonEscaped;
+    try {
+      System.out.println("Reading array of escaped and non escaped unicode chars.");
+      reader = Json.createReader(new StringReader(unicodeTextString));
+      JsonArray array = reader.readArray();
+      String actResult = array.getJsonString(0).getString();
+      pass = JSONP_Util.assertEquals(expResult, actResult);
+    } catch (Exception e) {
+      throw new Fault("readEscapeUnicodeControlCharsInArrayTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeUnicodeControlCharsInArrayTest Failed");
+  }
+
+  /*
+   * @testName: readEmptyObjectTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an empty object "{}" from stream. Use
+   * JsonReader.readObject() API call.
+   *
+   */
+  @Test
+  public void readEmptyObjectTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String expJsonText = "{}";
+      System.out.println("Testing read of " + expJsonText);
+      reader = Json.createReader(new StringReader(expJsonText));
+      JsonObject object = reader.readObject();
+      pass = JSONP_Util.assertEqualsEmptyObjectMap(object);
+    } catch (Exception e) {
+      throw new Fault("readEmptyObjectTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEmptyObjectTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeCharsInObjectTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an object from a resource file with special
+   * chars in data. Use JsonReader.readObject() API call. Test scenario: Read
+   * string of JSON text containing a JSON object from resource file with
+   * following data: { "specialChars" : "popeye\"\\\/\b\f\n\r\tolive" }
+   *
+   * These characters are backslash escape'd as follows: \" \\ \/ \b \f \n \r \t
+   *
+   * Create a JsonWriter to write above JsonObject to a string of JSON text.
+   * Re-read JsonWriter text back into a JsonObject Compare expected JSON object
+   * with actual JSON object for equality.
+   *
+   */
+  @Test
+  public void readEscapeCharsInObjectTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonObjectWithEscapeCharsData.json";
+    String expString = "popeye" + JSONP_Data.escapeCharsAsString + "olive";
+    try {
+
+      System.out.println("Reading contents of resource file " + resourceFile);
+      String readerContents = JSONP_Util
+          .getContentsOfResourceAsString(resourceFile);
+      System.out.println("readerContents=" + readerContents);
+
+      System.out.println("Testing read of resource contents: " + readerContents);
+      reader = Json.createReader(new StringReader(readerContents));
+      JsonObject expJsonObject = reader.readObject();
+
+      System.out.println("Dump of expJsonObject");
+      JSONP_Util.dumpJsonObject(expJsonObject);
+
+      System.out.println("Comparing JsonArray values with expected results.");
+      String actString = expJsonObject.getJsonString("escapeChars").getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+
+      System.out.println("Write the JsonObject 'expJsonObject' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(expJsonObject);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Create actJsonObject from read of writer contents: "
+          + writerContents);
+      reader = Json.createReader(new StringReader(writerContents));
+      JsonObject actJsonObject = reader.readObject();
+
+      System.out.println("Dump of actJsonObject");
+      JSONP_Util.dumpJsonObject(actJsonObject);
+
+      System.out.println("Compare expJsonObject and actJsonObject for equality");
+      if (!JSONP_Util.assertEqualsJsonObjects(expJsonObject, actJsonObject))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readEscapeCharsInObjectTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeCharsInObjectTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeUnicodeCharsInObjectTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an object with unicode chars escaped and not
+   * escaped. Use JsonReader.readObject() API call. Test scenario: Read string
+   * of JSON text containing a JSON object with the following data: {
+   * "unicodechars":"\\u0000\u00ff\\uff00\uffff" ]
+   *
+   * Notice unicode \u0000 and \uff00 is escaped but \u00ff and \uffff is not.
+   *
+   * Compare expected JSON String with actual JSON String for equality.
+   *
+   */
+  @Test
+  public void readEscapeUnicodeCharsInObjectTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String unicodeTextString = "{\"unicodechars\":\"\\u0000\u00ff\\uff00\uffff\"}";
+    String expResult = "\u0000\u00ff\uff00\uffff";
+    try {
+      System.out.println("Reading object of escaped and non escaped unicode chars.");
+      reader = Json.createReader(new StringReader(unicodeTextString));
+      JsonObject object = reader.readObject();
+      String actResult = object.getJsonString("unicodechars").getString();
+      pass = JSONP_Util.assertEquals(expResult, actResult);
+    } catch (Exception e) {
+      throw new Fault("readEscapeUnicodeCharsInObjectTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeUnicodeCharsInObjectTest Failed");
+  }
+
+  /*
+   * @testName: readEscapeUnicodeControlCharsInObjectTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an array with unicode control chars escaped.
+   * Use JsonReader.readObject() API call. Test scenario: Read string of JSON
+   * text containing unicode control chars escaped as a Json Object.
+   *
+   * Compare expected JSON String with actual JSON String for equality.
+   *
+   */
+  @Test
+  public void readEscapeUnicodeControlCharsInObjectTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String unicodeTextString = "{\"unicodechars\":\""
+        + JSONP_Data.unicodeControlCharsEscaped + "\"}";
+    String expResult = JSONP_Data.unicodeControlCharsNonEscaped;
+    try {
+      System.out.println("Reading array of escaped and non escaped unicode chars.");
+      reader = Json.createReader(new StringReader(unicodeTextString));
+      JsonObject object = reader.readObject();
+      String actResult = object.getJsonString("unicodechars").getString();
+      pass = JSONP_Util.assertEquals(expResult, actResult);
+    } catch (Exception e) {
+      throw new Fault("readEscapeUnicodeControlCharsInObjectTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readEscapeUnicodeControlCharsInObjectTest Failed");
+  }
+
+  /*
+   * @testName: readArrayTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of an array containing various types from stream.
+   * Use JsonReader.readArray() API call. [true, false, null, "booyah",
+   * 2147483647, 9223372036854775807, 1.7976931348623157E308,
+   * [true,false,null,"bingo",-2147483648,-9223372036854775808,4.9E-324],
+   * {"true":true,"false":false,"null":null,"bonga":"boo","int":1,"double":10.4}
+   * ] Test scenario: Read string of JSON text above consisting of a JSON array
+   * into a JsonArray object. Create an expected List of JsonArray values for
+   * use in test comparison. Compare expected list of JsonArray values with
+   * actual list for equality.
+   *
+   */
+  @Test
+  public void readArrayTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String jsonText = "[true,false,null,\"booyah\",2147483647,9223372036854775807,1.7976931348623157E308,"
+          + "[true,false,null,\"bingo\",-2147483648,-9223372036854775808,4.9E-324],"
+          + "{\"true\":true,\"false\":false,\"null\":null,\"bonga\":\"boo\",\"int\":1,"
+          + "\"double\":10.4}]";
+
+      System.out.println("Create the expected list of JsonArray values");
+      List<JsonValue> expList = new ArrayList<>();
+      expList.add(JsonValue.TRUE);
+      expList.add(JsonValue.FALSE);
+      expList.add(JsonValue.NULL);
+      expList.add(JSONP_Util.createJsonString("booyah"));
+      expList.add(JSONP_Util.createJsonNumber(Integer.MAX_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Long.MAX_VALUE));
+      expList.add(JSONP_Util.createJsonNumber(Double.MAX_VALUE));
+      JsonArray array = Json.createArrayBuilder().add(JsonValue.TRUE)
+          .add(JsonValue.FALSE).add(JsonValue.NULL).add("bingo")
+          .add(Integer.MIN_VALUE).add(Long.MIN_VALUE).add(Double.MIN_VALUE)
+          .build();
+      JsonObject object = Json.createObjectBuilder().add("true", JsonValue.TRUE)
+          .add("false", JsonValue.FALSE).add("null", JsonValue.NULL)
+          .add("bonga", "boo").add("int", 1).add("double", 10.4).build();
+      expList.add(array);
+      expList.add(object);
+
+      System.out.println("Testing read of " + jsonText);
+      reader = Json.createReader(new StringReader(jsonText));
+      JsonArray myJsonArray = reader.readArray();
+
+      List<JsonValue> actList = myJsonArray;
+      System.out.println(
+          "Compare actual list of JsonArray values with expected list of JsonArray values");
+      pass = JSONP_Util.assertEqualsList(expList, actList);
+    } catch (Exception e) {
+      throw new Fault("readArrayTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayTest Failed");
+  }
+
+  /*
+   * @testName: readArrayTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:184;
+   * JSONP:JAVADOC:178;
+   * 
+   * @test_Strategy: Test read of an array containing various types from stream.
+   * Use JsonReader.readArray() API call. [true, false, null, "booyah",
+   * 2147483647, 9223372036854775807, 1.7976931348623157E308,
+   * [true,false,null,"bingo",-2147483648,-9223372036854775808,4.9E-324],
+   * {"true":true,"false":false,"null":null,"bonga":"boo","int":1,"double":10.4}
+   * ] Test Scenario: Create an expected JsonArray of the above JSON array for
+   * use in test comparison. Create a JsonWriter to write the above JsonArray to
+   * a string of JSON text. Next call JsonReader to read the JSON text from the
+   * JsonWriter to a JsonArray object. Compare expected JsonArray object with
+   * actual JsonArray object for equality.
+   *
+   */
+  @Test
+  public void readArrayTest2() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      System.out.println("Create the expected list of JsonArray values");
+      JsonArray expJsonArray = Json.createArrayBuilder().add(JsonValue.TRUE)
+          .add(JsonValue.FALSE).add(JsonValue.NULL).add("booyah")
+          .add(Integer.MAX_VALUE).add(Long.MAX_VALUE).add(Double.MAX_VALUE)
+          .add(Json.createArrayBuilder().add(JsonValue.TRUE)
+              .add(JsonValue.FALSE).add(JsonValue.NULL).add("bingo")
+              .add(Integer.MIN_VALUE).add(Long.MIN_VALUE).add(Double.MIN_VALUE))
+          .add(Json.createObjectBuilder().add("true", JsonValue.TRUE)
+              .add("false", JsonValue.FALSE).add("null", JsonValue.NULL)
+              .add("bonga", "boo").add("int", 1).add("double", 10.4))
+          .build();
+
+      System.out.println("Write the JsonArray 'expJsonArray' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(expJsonArray);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String jsonText = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + jsonText);
+
+      System.out.println("Testing read of " + jsonText);
+      reader = Json.createReader(JSONP_Util.getInputStreamFromString(jsonText));
+      JsonArray actJsonArray = reader.readArray();
+
+      System.out.println("Compare expJsonArray and actJsonArray for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray);
+    } catch (Exception e) {
+      throw new Fault("readArrayTest2 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayTest2 Failed");
+  }
+
+  /*
+   * @testName: readArrayTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:449; JSONP:JAVADOC:184;
+   * JSONP:JAVADOC:419;
+   * 
+   * @test_Strategy: Test read of an array containing various types from stream.
+   * Use JsonReader.readArray() API call. [true, false, null, "booyah",
+   * 2147483647, 9223372036854775807,
+   * [true,false,null,"bingo",-2147483648,-9223372036854775808],
+   * {"true":true,"false":false,"null":null,"bonga":"boo","int":1} ] Test
+   * scenario: Read string of JSON text above consisting of a JSON array into a
+   * JsonArray object with an empty configuration. Create a JsonWriter to write
+   * the above JsonArray to a string of JSON text. Compare expected JSON text
+   * with actual JSON text for equality.
+   *
+   * Tests the following API's: JsonReader =
+   * Json.createReaderFactory(Map<String,?>).createReader(Reader) JsonArray
+   * array = JsonReader.readArray()
+   *
+   */
+  @Test
+  public void readArrayTest3() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String expJsonText = "[true,false,null,\"booyah\",2147483647,9223372036854775807,"
+          + "[true,false,null,\"bingo\",-2147483648,-9223372036854775808],"
+          + "{\"true\":true,\"false\":false,\"null\":null,\"bonga\":\"boo\",\"int\":1}]";
+
+      System.out.println("Testing read of " + expJsonText);
+      reader = Json.createReaderFactory(JSONP_Util.getEmptyConfig())
+          .createReader(new StringReader(expJsonText));
+      JsonArray myJsonArray = reader.readArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(myJsonArray);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonText = sWriter.toString();
+
+      System.out.println("Compare actual JSON text with expected JSON text");
+      pass = JSONP_Util.assertEqualsJsonText(expJsonText, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("readArrayTest3 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayTest3 Failed");
+  }
+
+  /*
+   * @testName: readArrayTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:420; JSONP:JAVADOC:184;
+   * JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of an array from a resource file with various
+   * amounts of data. Use JsonReader.readArray() API call. Test scenario: Read
+   * InputStream of JSON text containing a JSON array from resource file with
+   * various amounts of data use UTF-8 encoding. Create a JsonWriter to write
+   * above JsonArray to a string of JSON text. Re-read JsonWriter text back into
+   * a JsonArray Compare expected JSON array with actual JSON array for
+   * equality.
+   *
+   */
+  @Test
+  public void readArrayTest4() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonArrayWithAllTypesOfData.json";
+    try {
+      System.out.println(
+          "Read contents of InputStream from resource file: " + resourceFile);
+      Map<String, ?> config = JSONP_Util.getEmptyConfig();
+      reader = Json.createReaderFactory(config).createReader(
+          JSONP_Util.getInputStreamFromResource(resourceFile),
+          JSONP_Util.UTF_8);
+      JsonArray expJsonArray = reader.readArray();
+
+      System.out.println("Dump of expJsonArray");
+      JSONP_Util.dumpJsonArray(expJsonArray);
+
+      System.out.println("Write the JsonArray 'expJsonArray' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(expJsonArray);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Create actJsonArray from read of writer contents: "
+          + writerContents);
+      reader = Json.createReader(new StringReader(writerContents));
+      JsonArray actJsonArray = reader.readArray();
+
+      System.out.println("Dump of actJsonArray");
+      JSONP_Util.dumpJsonArray(actJsonArray);
+
+      System.out.println("Compare expJsonArray and actJsonArray for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray);
+    } catch (Exception e) {
+      throw new Fault("readArrayTest4 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayTest4 Failed");
+  }
+
+  /*
+   * @testName: readArrayTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:181;
+   * JSONP:JAVADOC:420; JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of an array from a resource file with lots of
+   * nesting. Use JsonReader.read() API call. Test scenario: Read InputStream of
+   * JSON text containing a JSON array from resource file with lots of nesting
+   * use UTF-8 encoding with empty configuration. Create a JsonWriter to write
+   * above JsonArray to a string of JSON text. Compare expected JSON text with
+   * actual JSON text for equality. Filter all text output to remove whitespace
+   * before comparison.
+   *
+   * Tests the following API's: JsonReader =
+   * Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset)
+   * JsonArray array = (JsonArray)JsonReader.read()
+   *
+   *
+   */
+  @Test
+  public void readArrayTest5() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonArrayWithLotsOfNestedObjectsData.json";
+    try {
+      System.out.println("Reading contents of resource file " + resourceFile);
+      String readerContents = JSONP_Util
+          .getContentsOfResourceAsString(resourceFile);
+      System.out.println("readerContents=" + readerContents);
+
+      // Create expected JSON text from resource contents filtered of whitespace
+      // for comparison
+      System.out.println("Filter readerContents of whitespace for comparison");
+      String expJsonText = JSONP_Util.removeWhitespace(readerContents);
+
+      System.out.println(
+          "Read contents of InputStream from resource file: " + resourceFile);
+      reader = Json.createReaderFactory(JSONP_Util.getEmptyConfig())
+          .createReader(JSONP_Util.getInputStreamFromResource(resourceFile),
+              JSONP_Util.UTF_8);
+      JsonArray myJsonArray = (JsonArray) reader.read();
+
+      System.out.println("Write the JsonArray 'myJsonArray' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(myJsonArray);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Dump contents of the JsonWriter as a String");
+      System.out.println("writerContents=" + writerContents);
+
+      System.out.println("Filter writerContents of whitespace for comparison");
+      String actJsonText = JSONP_Util.removeWhitespace(writerContents);
+
+      System.out.println("Compare actual JSON text with expected JSON text");
+      pass = JSONP_Util.assertEqualsJsonText(expJsonText, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("readArrayTest5 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayTest5 Failed");
+  }
+
+  /*
+   * @testName: readArrayEncodingTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:420; JSONP:JAVADOC:449;
+   * JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test read of a JsonArray from a resource file using both
+   * encodings of UTF-8 and UTF-16BE.
+   * 
+   * Test scenario: For each encoding read the appropriate resource file
+   * containing a string value. Call JsonArray.getJsonString() to get the value
+   * of the JsonString. Compare expected string with actual string for equality.
+   */
+  @Test
+  public void readArrayEncodingTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String expString = "a\u65e8\u452c\u8b9e\u6589\u5c57\u5217z";
+    String resourceFileUTF8 = "jsonArrayUTF8.json";
+    String resourceFileUTF16BE = "jsonArrayUTF16BE.json";
+    Map<String, ?> config = JSONP_Util.getEmptyConfig();
+    try {
+      System.out.println("Reading contents of resource file using UTF-8 encoding "
+          + resourceFileUTF8);
+      InputStream is = JSONP_Util.getInputStreamFromResource(resourceFileUTF8);
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_8);
+      JsonArray jsonArray = reader.readArray();
+      System.out.println("Comparing JsonArray values with expected results.");
+      String actString = jsonArray.getJsonString(0).getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readArrayEncodingTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    try {
+      System.out.println("Reading contents of resource file using UTF-16BE encoding "
+          + resourceFileUTF16BE);
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource(resourceFileUTF16BE);
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_16BE);
+      JsonArray jsonArray = reader.readArray();
+      System.out.println("Comparing JsonArray values with expected results.");
+      String actString = jsonArray.getJsonString(0).getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readArrayEncodingTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readArrayEncodingTest Failed");
+  }
+
+  /*
+   * @testName: readObjectTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an object containing various types from
+   * stream. Use JsonReader.readObject() API call. {"true":true, "false":false,
+   * "null":null, "booyah":"booyah", "int":2147483647,
+   * "long":9223372036854775807, "double":1.7976931348623157E308,
+   * "array":[true,false,null,"bingo",-2147483648,-9223372036854775808,4.9E-324]
+   * , "object":{"true":true,"false":false,"null":null,"bonga":"boo","int":1,
+   * "double":10.4} } Test scenario: Read string of JSON text above consisting
+   * of a JSON object into a JsonObject object. Create an expected map of
+   * JsonObject values for use in test comparison. Compare expected map of
+   * JsonObject values with actual map for equality.
+   *
+   */
+  @Test
+  public void readObjectTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String expJsonText = "{\"true\":true,\"false\":false,\"null\":null,\"booyah\":\"booyah\",\"int\":2147483647,"
+          + "\"long\":9223372036854775807,\"double\":1.7976931348623157E308,"
+          + "\"array\":[true,false,null,\"bingo\",-2147483648,-9223372036854775808,4.9E-324],"
+          + "\"object\":{\"true\":true,\"false\":false,\"null\":null,\"bonga\":\"boo\",\"int\":1,"
+          + "\"double\":10.4}}";
+
+      System.out.println("Create the expected map of JsonObject values");
+      Map<String, JsonValue> expMap = new HashMap<>();
+      expMap.put("true", JsonValue.TRUE);
+      expMap.put("false", JsonValue.FALSE);
+      expMap.put("null", JsonValue.NULL);
+      expMap.put("booyah", JSONP_Util.createJsonString("booyah"));
+      expMap.put("int", JSONP_Util.createJsonNumber(Integer.MAX_VALUE));
+      expMap.put("long", JSONP_Util.createJsonNumber(Long.MAX_VALUE));
+      expMap.put("double", JSONP_Util.createJsonNumber(Double.MAX_VALUE));
+      JsonArray array = Json.createArrayBuilder().add(JsonValue.TRUE)
+          .add(JsonValue.FALSE).add(JsonValue.NULL).add("bingo")
+          .add(Integer.MIN_VALUE).add(Long.MIN_VALUE).add(Double.MIN_VALUE)
+          .build();
+      JsonObject object = Json.createObjectBuilder().add("true", JsonValue.TRUE)
+          .add("false", JsonValue.FALSE).add("null", JsonValue.NULL)
+          .add("bonga", "boo").add("int", 1).add("double", 10.4).build();
+      expMap.put("array", array);
+      expMap.put("object", object);
+
+      System.out.println("Testing read of " + expJsonText);
+      reader = Json.createReader(new StringReader(expJsonText));
+      JsonObject myJsonObject = reader.readObject();
+
+      Map<String, JsonValue> actMap = myJsonObject;
+      System.out.println(
+          "Compare actual map of JsonObject values with expected map of JsonObject values");
+      pass = JSONP_Util.assertEqualsMap(expMap, actMap);
+    } catch (Exception e) {
+      throw new Fault("readObjectTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectTest Failed");
+  }
+
+  /*
+   * @testName: readObjectTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:178; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Test read of an object containing various types from
+   * stream. Use JsonReader.readObject() API call. {"true":true, "false":false,
+   * "null":null, "booyah":"booyah", "int":2147483647,
+   * "long":9223372036854775807, "double":1.7976931348623157E308,
+   * "array":[true,false,null,"bingo",-2147483648,-9223372036854775808,4.9E-324]
+   * , "object":{"true":true,"false":false,"null":null,"bonga":"boo","int":1,
+   * "double":10.4} } Test Scenario: Create an expected JsonObject of the above
+   * JSON object for use in test comparison. Create a JsonWriter to write the
+   * above JsonObject to a string of JSON text. Next call JsonReader to read the
+   * JSON text from the JsonWriter to a JsonObject object. Compare expected
+   * JsonObject object with actual JsonObject object for equality.
+   *
+   */
+  @Test
+  public void readObjectTest2() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      System.out.println("Create the expected list of JsonObject values");
+      JsonObject expJsonObject = Json.createObjectBuilder()
+          .add("true", JsonValue.TRUE).add("false", JsonValue.FALSE)
+          .add("null", JsonValue.NULL).add("booyah", "booyah")
+          .add("int", Integer.MAX_VALUE).add("long", Long.MAX_VALUE)
+          .add("double", Double.MAX_VALUE)
+          .add("array",
+              Json.createArrayBuilder().add(JsonValue.TRUE).add(JsonValue.FALSE)
+                  .add(JsonValue.NULL).add("bingo").add(Integer.MIN_VALUE)
+                  .add(Long.MIN_VALUE).add(Double.MIN_VALUE))
+          .add("object",
+              Json.createObjectBuilder().add("true", JsonValue.TRUE)
+                  .add("false", JsonValue.FALSE).add("null", JsonValue.NULL)
+                  .add("bonga", "boo").add("int", 1).add("double", 10.4))
+          .build();
+
+      System.out.println("Write the JsonObject 'expJsonObject' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(expJsonObject);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String jsonText = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + jsonText);
+
+      System.out.println("Testing read of " + jsonText);
+      reader = Json.createReader(JSONP_Util.getInputStreamFromString(jsonText));
+      JsonObject actJsonObject = reader.readObject();
+
+      System.out.println("Compare expJsonObject and actJsonObject for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(expJsonObject, actJsonObject);
+    } catch (Exception e) {
+      throw new Fault("readObjectTest2 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectTest2 Failed");
+  }
+
+  /*
+   * @testName: readObjectTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:185; JSONP:JAVADOC:419;
+   * JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of an object containing various types from
+   * stream. Use JsonReader.readObject() API call. {"true":true, "false":false,
+   * "null":null, "booyah":"booyah", "int":2147483647,
+   * "long":9223372036854775807,
+   * "array":[true,false,null,"bingo",-2147483648,-9223372036854775808],
+   * "object":{"true":true,"false":false,"null":null,"bonga":"boo","int":1} }
+   * Test scenario: Read string of JSON text above consisting of a JSON object
+   * into a JsonObject object with an empty configuration. Create a JsonWriter
+   * to write the above JsonObject to a string of JSON text. Compare expected
+   * JSON text with actual JSON text for equality.
+   *
+   * Tests the following API's: JsonReader =
+   * Json.createReaderFactory(Map<String,?>).createReader(Reader) JsonObject
+   * object = JsonReader.readObject()
+   *
+   *
+   */
+  @Test
+  public void readObjectTest3() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      String expJsonText = "{\"true\":true,\"false\":false,\"null\":null,\"booyah\":\"booyah\",\"int\":2147483647,\"long\":9223372036854775807,"
+          + "\"array\":[true,false,null,\"bingo\",-2147483648,-9223372036854775808],"
+          + "\"object\":{\"true\":true,\"false\":false,\"null\":null,\"bonga\":\"boo\",\"int\":1}}";
+
+      System.out.println("Testing read of " + expJsonText);
+      reader = Json.createReaderFactory(JSONP_Util.getEmptyConfig())
+          .createReader(new StringReader(expJsonText));
+      JsonObject myJsonObject = reader.readObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(myJsonObject);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonText = sWriter.toString();
+
+      System.out.println("Compare actual JSON text with expected JSON text");
+      pass = JSONP_Util.assertEqualsJsonText(expJsonText, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("readObjectTest3 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectTest3 Failed");
+  }
+
+  /*
+   * @testName: readObjectTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:420;
+   * JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of an object from a resource file with various
+   * amounts of data. Use JsonReader.readObject() API call. Test scenario: Read
+   * InputStream of JSON text containing a JSON object from resource file with
+   * various amounts of data use UTF-8 encoding. Create a JsonWriter to write
+   * above JsonObject to a string of JSON text. Re-read JsonWriter text back
+   * into a JsonObject Compare expected JSON object with actual JSON object for
+   * equality.
+   *
+   */
+  @Test
+  public void readObjectTest4() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonObjectWithAllTypesOfData.json";
+    try {
+      System.out.println(
+          "Read contents of InputStream from resource file: " + resourceFile);
+      Map<String, ?> config = JSONP_Util.getEmptyConfig();
+      reader = Json.createReaderFactory(config).createReader(
+          JSONP_Util.getInputStreamFromResource(resourceFile),
+          JSONP_Util.UTF_8);
+      JsonObject expJsonObject = reader.readObject();
+
+      System.out.println("Dump of expJsonObject");
+      JSONP_Util.dumpJsonObject(expJsonObject);
+
+      System.out.println("Write the JsonObject 'expJsonObject' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(expJsonObject);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Create actJsonObject from read of writer contents: "
+          + writerContents);
+      reader = Json.createReader(new StringReader(writerContents));
+      JsonObject actJsonObject = reader.readObject();
+
+      System.out.println("Dump of actJsonObject");
+      JSONP_Util.dumpJsonObject(actJsonObject);
+
+      System.out.println("Compare expJsonObject and actJsonObject for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(expJsonObject, actJsonObject);
+    } catch (Exception e) {
+      throw new Fault("readObjectTest4 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectTest4 Failed");
+  }
+
+  /*
+   * @testName: readObjectTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:181;
+   * JSONP:JAVADOC:420; JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of an object from a resource file with lots of
+   * nesting. Use JsonReader.read() API call. Test scenario: Read InputStream of
+   * JSON text containing a JSON object from resource file with lots of nesting
+   * use UTF-8 encoding with empty configuration. Create a JsonWriter to write
+   * above JsonObject to a string of JSON text. Compare expected JSON text with
+   * actual JSON text for equality. Filter all text output to remove whitespace
+   * before comparison.
+   *
+   * Tests the following API's: JsonReader =
+   * Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset)
+   * JsonReader.read() JsonObject object = (JsonObject)JsonReader.read()
+   *
+   *
+   */
+  @Test
+  public void readObjectTest5() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String resourceFile = "jsonObjectWithLotsOfNestedObjectsData.json";
+    try {
+      System.out.println("Reading contents of resource file " + resourceFile);
+      String readerContents = JSONP_Util
+          .getContentsOfResourceAsString(resourceFile);
+      System.out.println("readerContents=" + readerContents);
+
+      // Create expected JSON text from resource contents filtered of whitespace
+      // for comparison
+      System.out.println("Filter readerContents of whitespace for comparison");
+      String expJsonText = JSONP_Util.removeWhitespace(readerContents);
+
+      System.out.println(
+          "Read contents of InputStream from resource file: " + resourceFile);
+      reader = Json.createReaderFactory(JSONP_Util.getEmptyConfig())
+          .createReader(JSONP_Util.getInputStreamFromResource(resourceFile),
+              JSONP_Util.UTF_8);
+      JsonObject myJsonObject = (JsonObject) reader.read();
+
+      System.out.println("Write the JsonObject 'myJsonObject' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(myJsonObject);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+
+      System.out.println("Dump contents of the JsonWriter as a String");
+      System.out.println("writerContents=" + writerContents);
+
+      System.out.println("Filter writerContents of whitespace for comparison");
+      String actJsonText = JSONP_Util.removeWhitespace(writerContents);
+
+      System.out.println("Compare actual JSON text with expected JSON text");
+      pass = JSONP_Util.assertEqualsJsonText(expJsonText, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("readObjectTest5 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectTest5 Failed");
+  }
+
+  /*
+   * @testName: readObjectEncodingTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:420; JSONP:JAVADOC:185;
+   * JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Test read of a JsonObject from a resource file using both
+   * encodings of UTF-8 and UTF-16LE.
+   * 
+   * Test scenario: For each encoding read the appropriate resource file
+   * containing a string value. Call JsonObject.getJsonString() to get the value
+   * of the JsonString. Compare expected string with actual string for equality.
+   */
+  @Test
+  public void readObjectEncodingTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    String expString = "a\u65e8\u452c\u8b9e\u6589\u5c57\u5217z";
+    String resourceFileUTF8 = "jsonObjectUTF8.json";
+    String resourceFileUTF16LE = "jsonObjectUTF16LE.json";
+    try {
+      System.out.println("Reading contents of resource file using UTF-8 encoding "
+          + resourceFileUTF8);
+      InputStream is = JSONP_Util.getInputStreamFromResource(resourceFileUTF8);
+      Map<String, ?> config = JSONP_Util.getEmptyConfig();
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_8);
+      JsonObject jsonObject = reader.readObject();
+      System.out.println("Comparing JsonObject values with expected results.");
+      String actString = jsonObject.getJsonString("unicodeChars").getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readObjectEncodingTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    try {
+      System.out.println("Reading contents of resource file using UTF-16LE encoding "
+          + resourceFileUTF16LE);
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource(resourceFileUTF16LE);
+      Map<String, ?> config = JSONP_Util.getEmptyConfig();
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_16LE);
+      JsonObject jsonObject = reader.readObject();
+      System.out.println("Comparing JsonObject values with expected results.");
+      String actString = jsonObject.getJsonString("unicodeChars").getString();
+      if (!JSONP_Util.assertEquals(expString, actString))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("readObjectEncodingTest Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("readObjectEncodingTest Failed");
+  }
+
+  /*
+   * @testName: readUTFEncodedTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:420; JSONP:JAVADOC:185;
+   * JSONP:JAVADOC:449;
+   * 
+   * @test_Strategy: Tests the JsonReader reader. Verifies READING of the
+   * JsonObject defined in resource files:
+   *
+   * jsonObjectEncodingUTF8.json jsonObjectEncodingUTF16.json
+   * jsonObjectEncodingUTF16LE.json jsonObjectEncodingUTF16BE.json
+   * jsonObjectEncodingUTF32LE.json jsonObjectEncodingUTF32BE.json
+   * 
+   * Creates the JsonReader via the API:
+   *
+   * JsonReader reader =
+   * Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset)
+   *
+   * For each supported encoding supported by JSON RFC read the JsonObject and
+   * verify we get the expected results. The Charset encoding is passed in as
+   * argument for each encoding type read.
+   */
+  @Test
+  public void readUTFEncodedTests() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    Map<String, ?> config = JSONP_Util.getEmptyConfig();
+    try {
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-8]");
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF8.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF8.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-8");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_8);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-8 encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-16]");
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-16");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_16);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-16 encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-16LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16LE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16LE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-16LE");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_16LE);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-16LE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-16BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16BE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16BE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-16BE");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_16BE);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-16BE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-32LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32LE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32LE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-32LE");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_32LE);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-32LE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createReaderFactory(Map<String,?>).createReader(InputStream, Charset) as UTF-32BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32BE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32BE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream with character encoding UTF-32BE");
+      reader = Json.createReaderFactory(config).createReader(is,
+          JSONP_Util.UTF_32BE);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-32BE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("readUTFEncodedTests Failed");
+  }
+
+  /*
+   * @testName: readUTFEncodedTests2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:178; JSONP:JAVADOC:185;
+   * 
+   * @test_Strategy: Tests the JsonReader reader. Verifies READING of the
+   * JsonObject defined in resource files:
+   *
+   * jsonObjectEncodingUTF8.json jsonObjectEncodingUTF16LE.json
+   * jsonObjectEncodingUTF16BE.json jsonObjectEncodingUTF32LE.json
+   * jsonObjectEncodingUTF32BE.json
+   * 
+   * Creates the JsonReader via the API:
+   *
+   * JsonReader reader = Json.createReader(InputStream istream)
+   *
+   * For each supported encoding supported by JSON RFC read the JsonObject and
+   * verify we get the expected results. The character encoding of the stream is
+   * auto-detected and determined as per the RFC.
+   */
+  @Test
+  public void readUTFEncodedTests2() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      System.out.println("---------------------------------------------------");
+      System.out.println("TEST CASE [Json.createReader(InputStream) as UTF-8]");
+      System.out.println("---------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF8.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF8.json");
+      System.out.println(
+          "Create JsonReader from the InputStream and auto-detect character encoding UTF-8");
+      reader = Json.createReader(is);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-8 encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println("------------------------------------------------------");
+      System.out.println("TEST CASE [Json.createReader(InputStream) as UTF-16LE]");
+      System.out.println("------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16LE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16LE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream and auto-detect character encoding UTF-16LE");
+      reader = Json.createReader(is);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-16LE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println("------------------------------------------------------");
+      System.out.println("TEST CASE [Json.createReader(InputStream) as UTF-16BE]");
+      System.out.println("------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF16BE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF16BE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream and auto-detect character encoding UTF-16BE");
+      reader = Json.createReader(is);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-16BE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println("------------------------------------------------------");
+      System.out.println("TEST CASE [Json.createReader(InputStream) as UTF-32LE]");
+      System.out.println("------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32LE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32LE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream and auto-detect character encoding UTF-32LE");
+      reader = Json.createReader(is);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-32LE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    try {
+      System.out.println("------------------------------------------------------");
+      System.out.println("TEST CASE [Json.createReader(InputStream) as UTF-32BE]");
+      System.out.println("------------------------------------------------------");
+      System.out.println(
+          "Get InputStream from data file as resource (jsonObjectEncodingUTF32BE.json)");
+      InputStream is = JSONP_Util
+          .getInputStreamFromResource("jsonObjectEncodingUTF32BE.json");
+      System.out.println(
+          "Create JsonReader from the InputStream and auto-detect character encoding UTF-32BE");
+      reader = Json.createReader(is);
+      JsonObject jsonObject = reader.readObject();
+      if (!compareJsonObjectForUTFEncodedTests(jsonObject))
+        pass = false;
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing reading of UTF-32BE encoding: " + e);
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Exception e) {
+      }
+    }
+    if (!pass)
+      throw new Fault("readUTFEncodedTests2 Failed");
+  }
+
+  /*
+   * @testName: negativeObjectTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:413;
+   * 
+   * @test_Strategy: Test various Json Syntax Errors when reading a JsonObject.
+   * The tests trip various JsonParsingException/JsonException conditions when
+   * reading an object.
+   *
+   */
+  @Test
+  public void negativeObjectTests() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+
+    // Not an object []
+
+    try {
+      System.out.println("Testing for not an object '[]'");
+      reader = Json.createReader(new StringReader("[]"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonException");
+    } catch (JsonException e) {
+      System.out.println("Got expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Trip JsonParsingException for JsonReader.readObject() if incorrect
+    // representation for object
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if incorrect representation for object.");
+      System.out.println("Reading " + "{\"name\":\"value\",1,2,3}");
+      reader = Json
+          .createReader(new StringReader("{\"name\":\"value\",1,2,3}"));
+      JsonObject jsonObject = reader.readObject();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Missing [
+
+    try {
+      System.out.println("Testing for missing '['");
+      reader = Json.createReader(new StringReader("{1,2]}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing ]
+
+    try {
+      System.out.println("Testing for missing ']'");
+      reader = Json.createReader(new StringReader("{[1,2}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing {
+
+    try {
+      System.out.println("Testing for missing '{'");
+      reader = Json.createReader(new StringReader("}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing }
+
+    try {
+      System.out.println("Testing for missing '}'");
+      reader = Json.createReader(new StringReader("{"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 1");
+      reader = Json.createReader(new StringReader("{[5\"foo\"]}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 2");
+      reader = Json.createReader(new StringReader("{[5{}]}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 1");
+      reader = Json.createReader(new StringReader("{\"foo\":\"bar\"5}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 2");
+      reader = Json.createReader(new StringReader("{\"one\":1[]}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing key name in object element
+
+    try {
+      System.out.println("Testing for missing key name in object element");
+      reader = Json.createReader(new StringReader("{:\"bar\"}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing value name in object element
+
+    try {
+      System.out.println("Testing for missing value name in object element");
+      reader = Json.createReader(new StringReader("{\"foo\":}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a name
+
+    try {
+      System.out.println("Test for missing double quote on a name");
+      reader = Json.createReader(new StringReader("{name\" : \"value\"}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a value
+
+    try {
+      System.out.println("Test for missing double quote on a value");
+      reader = Json.createReader(new StringReader("{\"name\" : value\"}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 1
+
+    try {
+      System.out.println("Incorrect digit value -foo");
+      reader = Json.createReader(new StringReader("{\"number\" : -foo}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 2
+
+    try {
+      System.out.println("Incorrect digit value +foo");
+      reader = Json.createReader(new StringReader("{\"number\" : +foo}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 3
+
+    try {
+      System.out.println("Incorrect digit value -784foo");
+      reader = Json.createReader(new StringReader("{\"number\" : -784foo}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 4
+
+    try {
+      System.out.println("Incorrect digit value +784foo");
+      reader = Json.createReader(new StringReader("{\"number\" : +784foo}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 5
+
+    try {
+      System.out.println("Incorrect digit value 0.1E5E5");
+      reader = Json.createReader(new StringReader("{\"number\" : 0.1E5E5}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 6
+
+    try {
+      System.out.println("Incorrect digit value  0.F10");
+      reader = Json.createReader(new StringReader("{\"number\" : 0.F10}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 7
+
+    try {
+      System.out.println("Incorrect digit value string");
+      reader = Json.createReader(new StringReader("{\"number\" : string}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 8 (hex numbers invalid per JSON RFC)
+
+    try {
+      System.out.println("Incorrect digit value hex numbers invalid per JSON RFC");
+      reader = Json.createReader(new StringReader("{\"number\" : 0x137a}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 9 (octal numbers invalid per JSON RFC)
+
+    try {
+      System.out.println("Incorrect digit value octal numbers invalid per JSON RFC");
+      reader = Json.createReader(new StringReader("{\"number\" : 0137}"));
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    if (!pass)
+      throw new Fault("negativeObjectTests Failed");
+  }
+
+  /*
+   * @testName: negativeArrayTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:412;
+   * 
+   * @test_Strategy: Test various Json Syntax Errors when reading a JsonArray.
+   * The tests trip various JsonParsingException/JsonException conditions when
+   * reading an array.
+   *
+   */
+  @Test
+  public void negativeArrayTests() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+
+    // Not an array {}
+
+    try {
+      System.out.println("Testing for not an array '{}'");
+      reader = Json.createReader(new StringReader("{}"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonException");
+    } catch (JsonException e) {
+      System.out.println("Got expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Trip JsonParsingException for JsonReader.readArray() if incorrect
+    // representation for array
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.readArray() if incorrect representation for array.");
+      System.out.println("Reading " + "[foo,10,\"name\":\"value\"]");
+      reader = Json
+          .createReader(new StringReader("[foo,10,\"name\":\"value\"]"));
+      JsonArray jsonArray = reader.readArray();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Missing [
+
+    try {
+      System.out.println("Testing for missing '['");
+      reader = Json.createReader(new StringReader("]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing ]
+
+    try {
+      System.out.println("Testing for missing ']'");
+      reader = Json.createReader(new StringReader("["));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing {
+
+    try {
+      System.out.println("Testing for missing '{'");
+      reader = Json.createReader(new StringReader("[1,\"name\":\"value\"}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing }
+
+    try {
+      System.out.println("Testing for missing '}'");
+      reader = Json.createReader(new StringReader("[1,{\"name\":\"value\"]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 1");
+      reader = Json.createReader(new StringReader("[5\"foo\"]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 2");
+      reader = Json.createReader(new StringReader("[5{}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 1");
+      reader = Json.createReader(new StringReader("[{\"foo\":\"bar\"5}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 2");
+      reader = Json.createReader(new StringReader("[{\"one\":1[]}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing key name in object element
+
+    try {
+      System.out.println("Testing for missing key name in object element");
+      reader = Json.createReader(new StringReader("[{:\"bar\"}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing value name in object element
+
+    try {
+      System.out.println("Testing for missing value name in object element");
+      reader = Json.createReader(new StringReader("[{\"foo\":}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a name
+
+    try {
+      System.out.println("Test for missing double quote on a name");
+      reader = Json.createReader(new StringReader("[{name\" : \"value\"}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a value
+
+    try {
+      System.out.println("Test for missing double quote on a value");
+      reader = Json.createReader(new StringReader("[{\"name\" : value\"}]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 1
+
+    try {
+      System.out.println("Incorrect digit value -foo");
+      reader = Json.createReader(new StringReader("[-foo]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 2
+
+    try {
+      System.out.println("Incorrect digit value +foo");
+      reader = Json.createReader(new StringReader("[+foo]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 3
+
+    try {
+      System.out.println("Incorrect digit value -784foo");
+      reader = Json.createReader(new StringReader("[-784foo]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 4
+
+    try {
+      System.out.println("Incorrect digit value +784foo");
+      reader = Json.createReader(new StringReader("[+784foo]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 5
+
+    try {
+      System.out.println("Incorrect digit value 0.1E5E5");
+      reader = Json.createReader(new StringReader("[0.1E5E5]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 6
+
+    try {
+      System.out.println("Incorrect digit value  0.F10");
+      reader = Json.createReader(new StringReader("[0.F10]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 7
+
+    try {
+      System.out.println("Incorrect digit value string");
+      reader = Json.createReader(new StringReader("[string]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 8 (hex numbers invalid per JSON RFC)
+
+    try {
+      System.out.println("Incorrect digit value hex numbers invalid per JSON RFC");
+      reader = Json.createReader(new StringReader("[0x137a]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 9 (octal numbers invalid per JSON RFC)
+
+    try {
+      System.out.println("Incorrect digit value octal numbers invalid per JSON RFC");
+      reader = Json.createReader(new StringReader("[0137]"));
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    if (!pass)
+      throw new Fault("negativeArrayTests Failed");
+  }
+
+  /*
+   * @testName: illegalStateExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:218;
+   * JSONP:JAVADOC:220; JSONP:JAVADOC:183;
+   * 
+   * @test_Strategy: Test IllegalStateException test conditions.
+   *
+   */
+  @Test
+  public void illegalStateExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+
+    // IllegalStateException if reader.close() called before reader.read()
+    try {
+      reader = Json.createReader(new StringReader("{}"));
+      reader.close();
+      System.out.println(
+          "Calling reader.read() after reader.close() is called is illegal.");
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if reader.read() called after reader.readObject()
+    try {
+      reader = Json.createReader(new StringReader("{}"));
+      JsonObject value = reader.readObject();
+      System.out.println(
+          "Calling reader.readObject() after reader.readObject() was called is illegal.");
+      value = (JsonObject) reader.read();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // IllegalStateException if reader.read() called after reader.readArray()
+    try {
+      reader = Json.createReader(new StringReader("[]"));
+      JsonArray value = reader.readArray();
+      System.out.println(
+          "Calling reader.read() after reader.readArray() was called is illegal.");
+      value = (JsonArray) reader.read();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // IllegalStateException if reader.close() called before reader.readObject()
+    try {
+      reader = Json.createReader(new StringReader("{}"));
+      reader.close();
+      System.out.println(
+          "Calling reader.readObject() after reader.close() is called is illegal.");
+      JsonObject value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if reader.readObject() called after
+    // reader.readObject()
+    try {
+      reader = Json.createReader(new StringReader("{}"));
+      JsonObject value = reader.readObject();
+      System.out.println(
+          "Calling reader.readObject() after reader.readObject() was called is illegal.");
+      value = reader.readObject();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // IllegalStateException if reader.readArray() called after
+    // reader.readObject()
+    try {
+      reader = Json.createReader(new StringReader("{}"));
+      JsonObject obj = reader.readObject();
+      System.out.println(
+          "Calling reader.readArray() after reader.readObject() was called is illegal.");
+      JsonArray arr = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // IllegalStateException if reader.close() called before reader.readArray()
+    try {
+      reader = Json.createReader(new StringReader("[]"));
+      reader.close();
+      System.out.println(
+          "Calling reader.readArray() after reader.close() is called is illegal.");
+      JsonArray value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if reader.readArray() called after
+    // reader.readArray()
+    try {
+      reader = Json.createReader(new StringReader("[]"));
+      JsonArray value = reader.readArray();
+      System.out.println(
+          "Calling reader.readArray() after reader.readArray() was called is illegal.");
+      value = reader.readArray();
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // IllegalStateException if reader.readObject() called after
+    // reader.readArray()
+    try {
+      reader = Json.createReader(new StringReader("[]"));
+      JsonArray arr = reader.readArray();
+      System.out.println(
+          "Calling reader.readObject() after reader.readArray() was called is illegal.");
+      JsonObject obj = reader.readObject();
+      pass = false;
+      System.out.println("obj=" + obj);
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    if (!pass)
+      throw new Fault("illegalStateExceptionTests Failed");
+  }
+
+  /*
+   * @testName: negativeJsonStructureTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:96; JSONP:JAVADOC:97; JSONP:JAVADOC:411;
+   * 
+   * @test_Strategy: Test various Json Syntax Errors when reading a
+   * JsonStructure. The tests trip various JsonParsingException conditions when
+   * doing a read.
+   *
+   */
+  @Test
+  public void negativeJsonStructureTests() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+
+    // Trip JsonParsingException for JsonReader.read() if incorrect
+    // representation for array
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if incorrect representation for array.");
+      System.out.println("Reading " + "[foo,10,\"name\":\"value\"]");
+      reader = Json
+          .createReader(new StringReader("[foo,10,\"name\":\"value\"]"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if incorrect
+    // representation for object
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if incorrect representation for object.");
+      System.out.println("Reading " + "{\"name\":\"value\",1,2,3}");
+      reader = Json
+          .createReader(new StringReader("{\"name\":\"value\",1,2,3}"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // incorrect representation {]
+    try {
+      System.out.println("Testing for incorrect representation '{]'");
+      reader = Json.createReader(new StringReader("{]"));
+      System.out.println(
+          "Calling reader.read() with incorrect representation should throw JsonParsingException");
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Missing [
+
+    try {
+      System.out.println("Testing for missing '['");
+      reader = Json.createReader(new StringReader("{1,2]}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing ]
+
+    try {
+      System.out.println("Testing for missing ']'");
+      reader = Json.createReader(new StringReader("{[1,2}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing {
+
+    try {
+      System.out.println("Testing for missing '{'");
+      reader = Json.createReader(new StringReader("}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing }
+
+    try {
+      System.out.println("Testing for missing '}'");
+      reader = Json.createReader(new StringReader("{"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 1");
+      reader = Json.createReader(new StringReader("{[5\"foo\"]}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between array elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between array elements test case 2");
+      reader = Json.createReader(new StringReader("{[5{}]}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 1
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 1");
+      reader = Json.createReader(new StringReader("{\"foo\":\"bar\"5}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing , between object elements test case 2
+
+    try {
+      System.out.println("Testing for missing ',' between object elements test case 2");
+      reader = Json.createReader(new StringReader("{\"one\":1[]}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing key name in object element
+
+    try {
+      System.out.println("Testing for missing key name in object element");
+      reader = Json.createReader(new StringReader("{:\"bar\"}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing value name in object element
+
+    try {
+      System.out.println("Testing for missing value name in object element");
+      reader = Json.createReader(new StringReader("{\"foo\":}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a name
+
+    try {
+      System.out.println("Test for missing double quote on a name");
+      reader = Json.createReader(new StringReader("{name\" : \"value\"}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Missing double quote on a value
+
+    try {
+      System.out.println("Test for missing double quote on a value");
+      reader = Json.createReader(new StringReader("{\"name\" : value\"}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 1
+
+    try {
+      System.out.println("Incorrect digit value -foo");
+      reader = Json.createReader(new StringReader("{\"number\" : -foo}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 2
+
+    try {
+      System.out.println("Incorrect digit value +foo");
+      reader = Json.createReader(new StringReader("{\"number\" : +foo}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 3
+
+    try {
+      System.out.println("Incorrect digit value -784foo");
+      reader = Json.createReader(new StringReader("{\"number\" : -784foo}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 4
+
+    try {
+      System.out.println("Incorrect digit value +784foo");
+      reader = Json.createReader(new StringReader("{\"number\" : +784foo}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 5
+
+    try {
+      System.out.println("Incorrect digit value 0.1E5E5");
+      reader = Json.createReader(new StringReader("{\"number\" : 0.1E5E5}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 6
+
+    try {
+      System.out.println("Incorrect digit value  0.F10");
+      reader = Json.createReader(new StringReader("{\"number\" : 0.F10}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    // Incorrect digit value test case 7
+
+    try {
+      System.out.println("Incorrect digit value string");
+      reader = Json.createReader(new StringReader("{\"number\" : string}"));
+      JsonStructure value = reader.read();
+      pass = false;
+      System.err.println("Failed to throw JsonParsingException");
+    } catch (JsonParsingException e) {
+      System.out.println("Got expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+
+    if (!pass)
+      throw new Fault("negativeJsonStructureTests Failed");
+  }
+
+  /*
+   * @testName: jsonReaderIOErrorTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:182; JSONP:JAVADOC:217; JSONP:JAVADOC:219;
+   * JSONP:JAVADOC:410;
+   * 
+   * @test_Strategy: Tests for JsonException for testable i/o errors.
+   *
+   */
+  @Test
+  public void jsonReaderIOErrorTests() throws Fault {
+    boolean pass = true;
+
+    String jsonArrayText = "[\"name1\",\"value1\"]";
+    String jsonObjectText = "{\"name1\":\"value1\"}";
+
+    // Trip JsonException if there is an i/o error on JsonReader.close()
+    try {
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonReader.close().");
+      System.out.println("Reading object " + jsonObjectText);
+      InputStream is = JSONP_Util.getInputStreamFromString(jsonObjectText);
+      MyBufferedInputStream mbi = new MyBufferedInputStream(is);
+      try (JsonReader reader = Json.createReader(mbi)) {
+        JsonObject jsonObject = reader.readObject();
+        System.out.println("jsonObject=" + jsonObject);
+        mbi.setThrowIOException(true);
+        System.out.println("Calling JsonReader.close()");
+        mbi.setThrowIOException(true);
+      }
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException for JsonReader.read() if i/o error
+    try {
+      System.out.println("Trip JsonException for JsonReader.read() if i/o error.");
+      System.out.println("Reading array " + jsonArrayText);
+      MyBufferedReader mbr = new MyBufferedReader(
+          new StringReader(jsonArrayText));
+      JsonReader reader = Json.createReader(mbr);
+      mbr.setThrowIOException(true);
+      System.out.println("Calling JsonReader.read()");
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException for JsonReader.readArray() if i/o error
+    try {
+      System.out.println("Trip JsonException for JsonReader.readArray() if i/o error.");
+      System.out.println("Reading array " + jsonArrayText);
+      MyBufferedReader mbr = new MyBufferedReader(
+          new StringReader(jsonArrayText));
+      JsonReader reader = Json.createReader(mbr);
+      mbr.setThrowIOException(true);
+      System.out.println("Calling JsonReader.readArray()");
+      JsonArray jsonArray = reader.readArray();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException for JsonReader.readObject() if i/o error
+    try {
+      System.out.println("Trip JsonException for JsonReader.read() if i/o error.");
+      System.out.println("Reading object " + jsonObjectText);
+      MyBufferedReader mbr = new MyBufferedReader(
+          new StringReader(jsonObjectText));
+      JsonReader reader = Json.createReader(mbr);
+      mbr.setThrowIOException(true);
+      System.out.println("Calling JsonReader.readObject()");
+      JsonObject jsonObject = reader.readObject();
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonReaderIOErrorTests Failed");
+  }
+
+  /*
+   * @testName: invalidLiteralNamesTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:97; JSONP:JAVADOC:411;
+   * 
+   * @test_Strategy: This test trips various JsonParsingException conditions
+   * when reading an uppercase literal name that must be lowercase per JSON RFC
+   * for the literal values (true, false or null).
+   *
+   */
+  @Test
+  public void invalidLiteralNamesTest() throws Fault {
+    boolean pass = true;
+    JsonReader reader;
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal TRUE
+    // instead of true
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal TRUE instead of true.");
+      System.out.println("Reading " + "[TRUE]");
+      reader = Json.createReader(new StringReader("[TRUE]"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal FALSE
+    // instead of false
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal FALSE instead of false.");
+      System.out.println("Reading " + "[FALSE]");
+      reader = Json.createReader(new StringReader("[FALSE]"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal NULL
+    // instead of null
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal NULL instead of null.");
+      System.out.println("Reading " + "[NULL]");
+      reader = Json.createReader(new StringReader("[NULL]"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal TRUE
+    // instead of true
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal TRUE instead of true.");
+      System.out.println("Reading " + "{\"true\":TRUE}");
+      reader = Json.createReader(new StringReader("{\"true\":TRUE}"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal FALSE
+    // instead of false
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal FALSE instead of false.");
+      System.out.println("Reading " + "{\"false\":FALSE}");
+      reader = Json.createReader(new StringReader("{\"false\":FALSE}"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonParsingException for JsonReader.read() if invalid liternal NULL
+    // instead of null
+    try {
+      System.out.println(
+          "Trip JsonParsingException for JsonReader.read() if invalid liternal NULL instead of null.");
+      System.out.println("Reading " + "{\"null\":NULL}");
+      reader = Json.createReader(new StringReader("{\"null\":NULL}"));
+      JsonStructure jsonStructure = reader.read();
+      System.err.println("Did not get expected JsonParsingException");
+      pass = false;
+    } catch (JsonParsingException e) {
+      System.out.println("Caught expected JsonParsingException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("invalidLiteralNamesTest Failed");
+  }
+
+  /*
+   * @testName: jsonReader11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:646; JSONP:JAVADOC:583; JSONP:JAVADOC:584;
+   * JSONP:JAVADOC:585; JSONP:JAVADOC:586; JSONP:JAVADOC:587; JSONP:JAVADOC:588;
+   * JSONP:JAVADOC:662; JSONP:JAVADOC:663; JSONP:JAVADOC:664; JSONP:JAVADOC:665;
+   * JSONP:JAVADOC:666; JSONP:JAVADOC:667;
+   * 
+   * @test_Strategy: Tests JsonReader API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonReader11Test() throws Fault {
+    Reader readerTest = new Reader();
+    final TestResult result = readerTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/Reader.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/Reader.java
new file mode 100644
index 0000000..e400ca2
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonreadertests/Reader.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonreadertests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.SimpleValues;
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import jakarta.json.Json;
+import jakarta.json.JsonException;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonParsingException;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for {@link JsonReader}.
+ */
+public class Reader {
+
+  /** Tests input data. */
+  private static final Object[] VALUES = new Object[] { OBJ_VALUE, // readValue()
+                                                                   // for
+                                                                   // JsonObject
+      createEmptyArrayWithStr(), // readValue() for simple JsonArray
+      STR_VALUE, // readValue() for String
+      INT_VALUE, // readValue() for int
+      LNG_VALUE, // readValue() for long
+      DBL_VALUE, // readValue() for double
+      BIN_VALUE, // readValue() for BigInteger
+      BDC_VALUE, // readValue() for BigDecimal
+      BOOL_VALUE, // readValue() for boolean
+      null // readValue() for null
+  };
+
+  /**
+   * Creates an instance of JavaScript Object Notation (JSON) compatibility
+   * tests for {@link JsonReader}.
+   */
+  Reader() {
+    super();
+  }
+
+  /**
+   * {@link JsonReader} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonReader API methods added in JSON-P 1.1.");
+    System.out.println("JsonReader API methods added in JSON-P 1.1.");
+    testReadValue(result);
+    testDoubleReadValue(result);
+    testIOExceptionOnReadValue(result);
+    testReadInvalidValue(result);
+    return result;
+  }
+
+  /**
+   * Test {@code JsonValue readValue()} method on all child types stored in
+   * source data.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testReadValue(final TestResult result) {
+    for (Object value : VALUES) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - readValue() for " + typeName + " in source data");
+      final JsonValue jsonValue = SimpleValues.toJsonValue(value);
+      final String data = JsonValueType.toStringValue(value);
+      System.out.println("    - Data: " + data);
+      final StringReader strReader = new StringReader(data);
+      JsonValue outValue = null;
+      try (final JsonReader reader = Json.createReader(strReader)) {
+        outValue = reader.readValue();
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("readValue()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+      if (operationFailed(jsonValue, outValue)) {
+        result.fail("readValue()", "Reader output " + valueToString(outValue)
+            + " value shall be " + valueToString(jsonValue));
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonValue readValue()} method with duplicated {@code JsonValue}
+   * read call. Second call is expected to throw {@code IllegalStateException}
+   * exception.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testDoubleReadValue(final TestResult result) {
+    for (Object value : VALUES) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(
+          " - duplicate readValue() for " + typeName + " in source data");
+      final String data = JsonValueType.toStringValue(value);
+      final StringReader strReader = new StringReader(data);
+      try (final JsonReader reader = Json.createReader(strReader)) {
+        // 1st attempt to read the data shall pass
+        reader.readValue();
+        try {
+          // 2nd attempt to read the data shall throw IllegalStateException
+          reader.readValue();
+          result.fail("readValue()",
+              "Duplicate call of readValue() shall throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+          System.out.println("    - Expected exception: " + ex.getMessage());
+        } catch (Throwable t) {
+          result.fail("readValue()",
+              "Duplicate call of readValue() shall throw IllegalStateException, not "
+                  + t.getClass().getSimpleName());
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("readValue()",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonValue readValue()} method with read call that causes
+   * IOException. IOException shall be encapsulated in JsonException.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  @SuppressWarnings("ConvertToTryWithResources")
+  private void testIOExceptionOnReadValue(final TestResult result) {
+    System.out.println(" - readValue() from already closed file reader");
+    File temp = null;
+    JsonReader reader;
+    // Close writer before calling write method.
+    try {
+      temp = File.createTempFile("testIOExceptionOnReadValue", ".txt");
+      System.out.println("    - Temporary file: " + temp.getAbsolutePath());
+      try (final FileWriter fileWriter = new FileWriter(temp)) {
+        fileWriter.write(JsonValueType.toStringValue(DEF_VALUE));
+      }
+      final FileReader fileReader = new FileReader(temp);
+      reader = Json.createReader(fileReader);
+      fileReader.close();
+    } catch (IOException ex) {
+      System.out.println("Caught IOException: " + ex.getLocalizedMessage());
+      result.fail("write(JsonValue)",
+          "Caught IOException: " + ex.getLocalizedMessage());
+      return;
+    } finally {
+      if (temp != null) {
+        temp.delete();
+      }
+    }
+    try {
+      reader.readValue();
+      result.fail("readValue()",
+          "Call of readValue() on already closed file reader shall throw JsonException");
+    } catch (JsonException ex) {
+      System.out.println("    - Expected exception: " + ex.getMessage());
+    } catch (Throwable t) {
+      result.fail("readValue()",
+          "Call of readValue() on already closed file reader shall throw JsonException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Test {@code JsonValue readValue()} method with read call on invalid JSON
+   * data. JsonParsingException shall be thrown when reading invalid data.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testReadInvalidValue(final TestResult result) {
+    System.out.println(" - readValue() on invalid JSON data");
+    // Invalid JSON: starting an array, closing an object.
+    final String data = "[" + SimpleValues.toJsonValue(DEF_VALUE) + "}";
+    final StringReader strReader = new StringReader(data);
+    JsonValue outValue = null;
+    try (final JsonReader reader = Json.createReader(strReader)) {
+      reader.readValue();
+      result.fail("readValue()",
+          "Call of readValue() on invalid data shall throw JsonParsingException");
+    } catch (JsonParsingException ex) {
+      System.out.println("    - Expected exception: " + ex.getMessage());
+    } catch (Throwable t) {
+      result.fail("readValue()",
+          "Call of readValue() on invalid data shall throw JsonParsingException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstreamingtests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstreamingtests/ClientTests.java
new file mode 100644
index 0000000..188e46d
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstreamingtests/ClientTests.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonstreamingtests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /*
+   * @testName: streamingTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:97; JSONP:JAVADOC:106; JSONP:JAVADOC:107;
+   * JSONP:JAVADOC:184;
+   * 
+   * @test_Strategy: Test Scenario: Generate stream of Json Text containing a
+   * JsonArray Compare actual Json Text generated with expected Json Text for
+   * equality Test passes if both JsonArray comparisons of Json Text are equal.
+   *
+   */
+  @Test
+  public void streamingTest1() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      // Set expected result
+      String expJsonText = "[1,2,3,4,5,6,7,8,9,10]";
+      System.out.println("expJsonText=" + expJsonText);
+
+      System.out.println("Generate stream of Json Text containing a JsonArray");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write(1).write(2).write(3).write(4).write(5)
+          .write(6).write(7).write(8).write(9).write(10).writeEnd();
+      generator.close();
+
+      // Get actual result
+      String actJsonText = JSONP_Util.removeWhitespace(sWriter.toString());
+      System.out.println("actJsonText=" + actJsonText);
+
+      System.out.println("Compare expJsonText and actJsonText for equality");
+      pass = JSONP_Util.assertEqualsJsonText(expJsonText, actJsonText);
+    } catch (Exception e) {
+      throw new Fault("streamingTest1 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("streamingTest1 Failed");
+  }
+
+  /*
+   * @testName: streamingTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:97; JSONP:JAVADOC:106; JSONP:JAVADOC:107;
+   * JSONP:JAVADOC:131;
+   * 
+   * @test_Strategy: Test Scenario: Generate data containing a JsonArray to a
+   * Writer stream. Read data from Writer stream containing a JsonArray. Write
+   * JsonArray out to a Writer stream. Re-read data from Writer stream
+   * containing a JsonArray. Compare initial JsonArray with subsequent JsonArray
+   * for equality. Test passes if both JsonArrays are equal.
+   *
+   */
+  @Test
+  public void streamingTest2() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    try {
+      System.out.println("Generate data containing a JsonArray");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartArray().write(2).write(4).write(6).write(8).write(10)
+          .writeEnd();
+      generator.close();
+
+      System.out.println("Read data from Writer stream containing a JsonArray");
+      reader = Json.createReader(new StringReader(sWriter.toString()));
+      JsonArray expJsonArray = reader.readArray();
+
+      System.out.println("Dump of expJsonArray");
+      JSONP_Util.dumpJsonArray(expJsonArray);
+
+      System.out.println("Write JsonArray out to a Writer stream");
+      sWriter = new StringWriter();
+      JsonWriter writer = Json.createWriter(sWriter);
+      writer.writeArray(expJsonArray);
+      System.out.println("Close JsonWriter");
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+      System.out.println("writerContents=" + writerContents);
+
+      System.out.println("Re-read data from Writer stream containing a JsonArray");
+      reader = Json.createReader(new StringReader(writerContents));
+      JsonArray actJsonArray = reader.readArray();
+
+      System.out.println("Dump of actJsonArray");
+      JSONP_Util.dumpJsonArray(actJsonArray);
+
+      System.out.println("Compare expJsonArray and actJsonArray for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(expJsonArray, actJsonArray);
+    } catch (Exception e) {
+      throw new Fault("streamingTest2 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("streamingTest2 Failed");
+  }
+
+  /*
+   * @testName: streamingTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:97; JSONP:JAVADOC:106; JSONP:JAVADOC:110;
+   * JSONP:JAVADOC:131; JSONP:JAVADOC:172;
+   * 
+   * @test_Strategy: Test Scenario: Generate data containing a JsonObject to a
+   * Write stream. Read data from Writer stream containing a JsonObject. Write
+   * JsonObject out to a Writer stream. Parse data from Writer stream containing
+   * a JsonObject stream. Test passes if parsing JsonObject events are correct.
+   *
+   */
+  @Test
+  public void streamingTest3() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    JsonParser parser = null;
+    try {
+      System.out.println("Generate data containing a JsonObject");
+      StringWriter sWriter = new StringWriter();
+      JsonGenerator generator = Json.createGenerator(sWriter);
+      generator.writeStartObject().write("two", 2).write("false", false)
+          .writeEnd();
+      generator.close();
+
+      System.out.println("Read data from Writer stream containing a JsonObject");
+      reader = Json.createReader(new StringReader(sWriter.toString()));
+      JsonObject expJsonObject = reader.readObject();
+
+      System.out.println("Dump of expJsonObject");
+      JSONP_Util.dumpJsonObject(expJsonObject);
+
+      System.out.println("Write JsonObject out to a Writer stream");
+      sWriter = new StringWriter();
+      JsonWriter writer = Json.createWriter(sWriter);
+      writer.writeObject(expJsonObject);
+      System.out.println("Close JsonWriter");
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = sWriter.toString();
+      System.out.println("writerContents=" + writerContents);
+
+      System.out.println("Parse data from Writer stream containing a JsonObject");
+      parser = Json
+          .createParser(JSONP_Util.getInputStreamFromString((writerContents)));
+
+      JSONP_Util.resetParseErrs();
+      JSONP_Util.testEventType(parser, JsonParser.Event.START_OBJECT);
+      JSONP_Util.testKeyIntegerValue(parser, "two", 2);
+      JSONP_Util.testKeyFalseValue(parser, "false");
+      JSONP_Util.testEventType(parser, JsonParser.Event.END_OBJECT);
+      int parseErrs = JSONP_Util.getParseErrs();
+      if (parseErrs != 0) {
+        System.err.println("There were " + parseErrs + " parser errors that occurred.");
+        pass = false;
+      }
+
+    } catch (Exception e) {
+      throw new Fault("streamingTest3 Failed: ", e);
+    } finally {
+      if (reader != null)
+        reader.close();
+    }
+    if (!pass)
+      throw new Fault("streamingTest3 Failed");
+  }
+
+  /*
+   * @testName: streamingTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:178; JSONP:JAVADOC:187; JSONP:JAVADOC:110;
+   * JSONP:JAVADOC:168;
+   * 
+   * @test_Strategy: Test Scenario: Generate data containing a JsonObject to an
+   * OutputStream. Compare expected JSON text from what was generated. Read data
+   * from InputStream containing a JsonObject. Write JsonObject again out to an
+   * OutputStream. Compare again expected JSON text from what was generated.
+   * Test passes if JSON text comparisons are correct.
+   *
+   */
+  @Test
+  public void streamingTest4() throws Fault {
+    boolean pass = true;
+    JsonReader reader = null;
+    JsonParser parser = null;
+    String expJsonText = "{\"two\":2,\"false\":false}";
+    try {
+      System.out.println("Generate data containing a JsonObject to an OutputStream");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonGenerator generator = Json.createGenerator(baos);
+      generator.writeStartObject().write("two", 2).write("false", false)
+          .writeEnd();
+      baos.close();
+      generator.close();
+
+      System.out.println("Compare JSON text generated to what is expected.");
+      if (!JSONP_Util.assertEqualsJsonText(expJsonText,
+          JSONP_Util.removeWhitespace(baos.toString("UTF-8"))))
+        pass = false;
+
+      System.out.println("Read data from InputStream containing a JsonObject");
+      reader = Json.createReader(
+          JSONP_Util.getInputStreamFromString(baos.toString("UTF-8")));
+      JsonObject expJsonObject = reader.readObject();
+      System.out.println("Close JsonReader");
+      reader.close();
+
+      System.out.println("Dump of expJsonObject");
+      JSONP_Util.dumpJsonObject(expJsonObject);
+
+      System.out.println("Write JsonObject back out to an OutputStream");
+      baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriter(baos);
+      writer.writeObject(expJsonObject);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String writerContents = baos.toString("UTF-8");
+      System.out.println("writerContents=" + writerContents);
+
+      System.out.println("Compare again JSON text generated to what is expected.");
+      if (!JSONP_Util.assertEqualsJsonText(expJsonText,
+          JSONP_Util.removeWhitespace(writerContents)))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("streamingTest4 Failed: ", e);
+    }
+
+    if (!pass)
+      throw new Fault("streamingTest4 Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstringtests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstringtests/ClientTests.java
new file mode 100644
index 0000000..06672ca
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonstringtests/ClientTests.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonstringtests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonStringEqualsTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:254;
+   * 
+   * @test_Strategy: Tests JsonString equals method. Create 2 equal JsonStrings
+   * and compare them for equality and expect true. Create 2 non-equal
+   * JsonStrings and compare them for equality and expect false.
+   */
+  @Test
+  public void jsonStringEqualsTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonString 1 for testing");
+      JsonString string1 = (JsonString) JSONP_Util
+          .createJsonString("Hello World");
+      System.out.println("string1=" + JSONP_Util.toStringJsonString(string1));
+
+      System.out.println("Create sample JsonString 2 for testing");
+      JsonString string2 = JSONP_Util.createJsonString("Hello World");
+      System.out.println("string2=" + JSONP_Util.toStringJsonString(string2));
+
+      System.out.println(
+          "Call JsonString.equals() to compare 2 equal JsonStrings and expect true");
+      if (string1.equals(string2)) {
+        System.out.println("JsonStrings are equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonStrings are not equal - unexpected.");
+      }
+
+      System.out.println("Create sample JsonString 1 for testing");
+      string1 = JSONP_Util.createJsonString("Hello World");
+      System.out.println("string1=" + JSONP_Util.toStringJsonString(string1));
+
+      System.out.println("Create sample JsonString 2 for testing");
+      string2 = JSONP_Util.createJsonString("Hello USA");
+      System.out.println("string2=" + JSONP_Util.toStringJsonString(string2));
+
+      System.out.println(
+          "Call JsonString.equals() to compare 2 equal JsonStrings and expect false");
+      if (!string1.equals(string2)) {
+        System.out.println("JsonStrings are not equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonStrings are equal - unexpected.");
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonStringEqualsTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonStringEqualsTest Failed");
+  }
+
+  /*
+   * @testName: jsonStringHashCodeTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:255;
+   * 
+   * @test_Strategy: Tests JsonString equals method. Create 2 equal JsonStrings
+   * and compare them for hashcode and expect true. Create 2 non-equal
+   * JsonStrings and compare them for hashcode and expect false.
+   */
+  @Test
+  public void jsonStringHashCodeTest() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonString 1 for testing");
+      JsonString string1 = JSONP_Util.createJsonString("Hello World");
+      System.out.println("string1=" + JSONP_Util.toStringJsonString(string1));
+      System.out.println("string1.hashCode()=" + string1.hashCode());
+
+      System.out.println("Create sample JsonString 2 for testing");
+      JsonString string2 = JSONP_Util.createJsonString("Hello World");
+      System.out.println("string2=" + JSONP_Util.toStringJsonString(string2));
+      System.out.println("string2.hashCode()=" + string2.hashCode());
+
+      System.out.println(
+          "Call JsonString.hashCode() to compare 2 equal JsonStrings and expect true");
+      if (string1.hashCode() == string2.hashCode()) {
+        System.out.println("JsonStrings hashCode are equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonStrings hashCode are not equal - unexpected.");
+      }
+
+      System.out.println("Create sample JsonString 1 for testing");
+      string1 = JSONP_Util.createJsonString("Hello World");
+      System.out.println("string1=" + JSONP_Util.toStringJsonString(string1));
+      System.out.println("string1.hashCode()=" + string1.hashCode());
+
+      System.out.println("Create sample JsonString 2 for testing");
+      string2 = JSONP_Util.createJsonString("Hello USA");
+      System.out.println("string2=" + JSONP_Util.toStringJsonString(string2));
+      System.out.println("string2.hashCode()=" + string2.hashCode());
+
+      System.out.println(
+          "Call JsonString.hashCode() to compare 2 equal JsonStrings and expect false");
+      if (string1.hashCode() != string2.hashCode()) {
+        System.out.println("JsonStrings hashCode are not equal - expected.");
+      } else {
+        pass = false;
+        System.err.println("JsonStrings hashCode are equal - unexpected.");
+      }
+    } catch (Exception e) {
+      throw new Fault("jsonStringHashCodeTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonStringHashCodeTest Failed");
+  }
+
+  /*
+   * @testName: jsonStringGetCharsTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:383;
+   * 
+   * @test_Strategy: Tests JsonString getChars method.
+   */
+  @Test
+  public void jsonStringGetCharsTest() throws Fault {
+    boolean pass = true;
+    String helloWorld = "Hello World";
+
+    try {
+      System.out.println("Create sample JsonString for testing");
+      JsonString string = JSONP_Util.createJsonString(helloWorld);
+      System.out.println("string=" + JSONP_Util.toStringJsonString(string));
+
+      System.out.println(
+          "Call JsonString.getChars() to return the char sequence for the JSON string");
+      CharSequence cs = string.getChars();
+      System.out.println("charSequence=" + cs.toString());
+
+      System.out.println("Checking char sequence for equality to expected string contents");
+      if (!JSONP_Util.assertEquals(helloWorld, cs.toString()))
+        pass = false;
+
+      System.out.println("Checking char sequence for expected equality to string length");
+      if (!JSONP_Util.assertEquals(helloWorld.length(), cs.length()))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonStringGetCharsTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonStringGetCharsTest Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/ClientTests.java
new file mode 100644
index 0000000..7f60a54
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/ClientTests.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonvaluetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonValueTypesTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:102;
+   * 
+   * @test_Strategy: Test JsonValue.getValueType() API method call with all
+   * JsonValue types.
+   *
+   */
+  @Test
+  public void jsonValueTypesTest() throws Fault {
+    boolean pass = true;
+    try {
+
+      JsonValue.ValueType valueType;
+
+      // Testing JsonValue.FALSE case
+      System.out.println("Testing getValueType for JsonValue.FALSE value");
+      valueType = JsonValue.FALSE.getValueType();
+      if (valueType != JsonValue.ValueType.FALSE) {
+        System.err.println("Expected JSON FALSE value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON FALSE value");
+
+      // Testing JsonValue.TRUE case
+      System.out.println("Testing getValueType for JsonValue.TRUE value");
+      valueType = JsonValue.TRUE.getValueType();
+      if (valueType != JsonValue.ValueType.TRUE) {
+        System.err.println("Expected JSON TRUE value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON TRUE value");
+
+      // Testing JsonValue.NULL case
+      System.out.println("Testing getValueType for JsonValue.NULL value");
+      valueType = JsonValue.NULL.getValueType();
+      if (valueType != JsonValue.ValueType.NULL) {
+        System.err.println("Expected JSON NULL value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON NULL value");
+
+      // Testing JsonValue.String case
+      System.out.println("Testing getValueType for JsonValue.String value");
+      valueType = JSONP_Util.createJsonString("string").getValueType();
+      if (valueType != JsonValue.ValueType.STRING) {
+        System.err.println("Expected JSON STRING value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON STRING value");
+
+      // Testing JsonValue.Number case
+      System.out.println("Testing getValueType for JsonValue.Number value");
+      valueType = JSONP_Util.createJsonNumber(Integer.MAX_VALUE).getValueType();
+      if (valueType != JsonValue.ValueType.NUMBER) {
+        System.err.println("Expected JSON NUMBER value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON NUMBER value");
+
+      // Testing JsonValue.Array case
+      System.out.println("Testing getValueType for JsonValue.Array value");
+      valueType = JSONP_Util.createJsonArrayFromString("[]").getValueType();
+      if (valueType != JsonValue.ValueType.ARRAY) {
+        System.err.println("Expected JSON ARRAY value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON ARRAY value");
+
+      // Testing JsonValue.Object case
+      System.out.println("Testing getValueType for JsonValue.Object value");
+      valueType = JSONP_Util.createJsonObjectFromString("{}").getValueType();
+      if (valueType != JsonValue.ValueType.OBJECT) {
+        System.err.println("Expected JSON OBJECT value type but got instead " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got expected value type for JSON OBJECT value");
+
+    } catch (Exception e) {
+      throw new Fault("jsonValueTypesTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonValueTypesTest Failed");
+  }
+
+  /*
+   * @testName: jsonValueOfTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:103;
+   * 
+   * @test_Strategy: Test JsonValue.ValueType.valueOf() API method call with all
+   * JsonValue types.
+   *
+   */
+  @Test
+  public void jsonValueOfTest() throws Fault {
+    boolean pass = true;
+
+    String valueTypeStrings[] = { "ARRAY", "FALSE", "NULL", "NUMBER", "OBJECT",
+        "STRING", "TRUE" };
+
+    for (String valueTypeString : valueTypeStrings) {
+      JsonValue.ValueType valueType;
+      try {
+        System.out.println(
+            "Testing enum value for string constant name " + valueTypeString);
+        valueType = JsonValue.ValueType.valueOf(valueTypeString);
+        System.out.println("Got enum type " + valueType + " for enum string constant named "
+            + valueTypeString);
+      } catch (Exception e) {
+        System.err.println("Caught unexpected exception: " + e);
+        pass = false;
+      }
+
+    }
+
+    System.out.println("Testing negative test case for NullPointerException");
+    try {
+      JsonValue.ValueType.valueOf(null);
+      System.err.println("did not get expected NullPointerException");
+      pass = false;
+    } catch (NullPointerException e) {
+      System.out.println("Got expected NullPointerException");
+    } catch (Exception e) {
+      System.err.println("Got unexpected exception " + e);
+      pass = false;
+    }
+
+    System.out.println("Testing negative test case for IllegalArgumentException");
+    try {
+      JsonValue.ValueType.valueOf("INVALID");
+      System.err.println("did not get expected IllegalArgumentException");
+      pass = false;
+    } catch (IllegalArgumentException e) {
+      System.out.println("Got expected IllegalArgumentException");
+    } catch (Exception e) {
+      System.err.println("Got unexpected exception " + e);
+      pass = false;
+    }
+
+    if (!pass)
+      throw new Fault("jsonValueOfTest Failed");
+  }
+
+  /*
+   * @testName: jsonValuesTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:104;
+   * 
+   * @test_Strategy: Test JsonValue.ValueType.values() API method call and
+   * verify enums returned.
+   *
+   */
+  @Test
+  public void jsonValuesTest() throws Fault {
+    boolean pass = true;
+
+    System.out.println(
+        "Testing API method JsonValue.ValueType.values() to return array of enums.");
+    JsonValue.ValueType[] values = JsonValue.ValueType.values();
+
+    for (JsonValue.ValueType valueType : values) {
+      String valueString = JSONP_Util.getValueTypeString(valueType);
+      if (valueString == null) {
+        System.err.println("Got no value for enum " + valueType);
+        pass = false;
+      } else
+        System.out.println("Got " + valueString + " for enum " + valueType);
+    }
+
+    if (!pass)
+      throw new Fault("jsonValuesTest Failed");
+  }
+
+  /*
+   * @testName: jsonValueToStringTest
+   * 
+   * @assertion_ids: JSONP:JAVADOC:288;
+   * 
+   * @test_Strategy: Test JsonValue.toString() API method call with various
+   * JsonValue types.
+   *
+   */
+  @Test
+  public void jsonValueToStringTest() throws Fault {
+    boolean pass = true;
+    try {
+      String stringValue;
+      JsonValue jsonValue;
+
+      // Testing JsonValue.FALSE case
+      System.out.println("Testing JsonValue.toString() for JsonValue.FALSE value");
+      stringValue = JsonValue.FALSE.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("false")) {
+        System.err.println("Expected false");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonValue.TRUE case
+      System.out.println("Testing JsonValue.toString() for JsonValue.TRUE value");
+      stringValue = JsonValue.TRUE.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("true")) {
+        System.err.println("Expected true");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonValue.NULL case
+      System.out.println("Testing JsonValue.toString() for JsonValue.NULL value");
+      stringValue = JsonValue.NULL.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("null")) {
+        System.err.println("Expected null");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonString case
+      System.out.println("Testing JsonValue.toString() for JsonString value");
+      jsonValue = JSONP_Util.createJsonString("string");
+      stringValue = jsonValue.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("\"string\"")) {
+        System.err.println("Expected \"string\"");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonNumber case
+      System.out.println("Testing JsonValue.toString() for JsonNumber value");
+      jsonValue = JSONP_Util.createJsonNumber(10);
+      stringValue = jsonValue.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("10")) {
+        System.err.println("Expected 10");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonArray case
+      System.out.println("Testing JsonValue.toString() for JsonArray value");
+      jsonValue = JSONP_Util.createJsonArrayFromString("[]");
+      stringValue = jsonValue.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("[]")) {
+        System.err.println("Expected []");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+      // Testing JsonObject case
+      System.out.println("Testing JsonValue.toString() for JsonObject value");
+      jsonValue = JSONP_Util.createJsonObjectFromString("{}");
+      stringValue = jsonValue.toString();
+      System.out.println("stringValue=" + stringValue);
+      if (!stringValue.equals("{}")) {
+        System.err.println("Expected {}");
+        pass = false;
+      } else {
+        System.out.println("Got " + stringValue);
+      }
+
+    } catch (Exception e) {
+      throw new Fault("jsonValueToStringTest Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonValueToStringTest Failed");
+  }
+
+  /*
+   * @testName: jsonValue11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:648; JSONP:JAVADOC:649;
+   * 
+   * @test_Strategy: Tests JsonValue API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonValue11Test() throws Fault {
+    Value valueTest = new Value();
+    final TestResult result = valueTest.test();
+    result.eval();
+  }
+
+  /*
+   * @testName: jsonStructure11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:647;
+   * 
+   * @test_Strategy: Tests JsonStructure API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonStructure11Test() throws Fault {
+    Structure structTest = new Structure();
+    final TestResult result = structTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Structure.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Structure.java
new file mode 100644
index 0000000..629dd3e
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Structure.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonvaluetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.PointerRFCObject.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for
+ * {@link JsonStructure}. RFC 6901 JSON Pointer is being passed to
+ * {@code JsonValue getValue(String)} method so whole JSON Pointer resolving
+ * sample is being used to test this method.
+ */
+public class Structure {
+  /**
+   * Creates an instance of JavaScript Object Notation (JSON) compatibility
+   * tests for {@link JsonStructure}.
+   */
+  Structure() {
+    super();
+  }
+
+  /**
+   * {@link JsonStructure} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonStructure API methods added in JSON-P 1.1.");
+    System.out.println("JsonStructure API methods added in JSON-P 1.1.");
+    testResolveWholeDocument(result);
+    testResolveEmptyName(result);
+    testResolveSimpleArray(result);
+    testResolveSimpleArrayItems(result);
+    testResolvePathWithEncodedSlash(result);
+    testResolvePathWithSlash(result);
+    testResolvePathWithPercent(result);
+    testResolvePathWithCaret(result);
+    testResolvePathWithVerticalBar(result);
+    testResolvePathWithBackSlash(result);
+    testResolvePathWithDoubleQuotes(result);
+    testResolvePathWithSpace(result);
+    testResolvePathWithTilde(result);
+    testResolvePathWithEncodedTilde(result);
+    testResolvePathWithEncodedTildeOne(result);
+    testResolveValidNumericIndexInArray(result);
+    testResolveMemberAfterLastInArray(result);
+    testResolveNonNumericIndexInArray(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolving for the whole document path using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveWholeDocument(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = value;
+    verifyGetValue(result, check, value, RFC_KEY_WHOLE);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "": 0} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveEmptyName(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL2);
+    verifyGetValue(result, check, value, RFC_PTR2);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "foo": ["bar", "baz"]} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveSimpleArray(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = RFC_VAL1;
+    verifyGetValue(result, check, value, RFC_PTR1);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "foo": ["bar", "baz"]} array
+   * elements using {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveSimpleArrayItems(final TestResult result) {
+    final String[] itemPtrs = new String[] { RFC_PTR1_ITEM1, RFC_PTR1_ITEM2 };
+    final String[] itemVals = new String[] { RFC_VAL1_ITEM1, RFC_VAL1_ITEM2 };
+    final JsonObject value = createRFC6901Object();
+    for (int i = 0; i < itemPtrs.length; i++) {
+      final JsonValue check = Json.createValue(itemVals[i]);
+      verifyGetValue(result, check, value, itemPtrs[i]);
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "a/b": 1} using
+   * {@code JsonValue getValue(String)}. Character {@code '/'} is encoded as
+   * {@code "~1"} string.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedSlash(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL3);
+    verifyGetValue(result, check, value, RFC_PTR3_ENC);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "a/b": 1} using
+   * {@code JsonValue getValue(String)}. Character {@code '/'} is not encoded as
+   * {@code "~1"} string. This results in invalid {@code "/a/b"} path and
+   * resolving such path must throw an exception.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithSlash(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    verifyGetValueFail(result, value, RFC_PTR3);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "c%d": 2} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithPercent(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL4);
+    verifyGetValue(result, check, value, RFC_PTR4);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "e^f": 3} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithCaret(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL5);
+    verifyGetValue(result, check, value, RFC_PTR5);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "g|h": 4} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithVerticalBar(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL6);
+    verifyGetValue(result, check, value, RFC_PTR6);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "i\\j": 5} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithBackSlash(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL7);
+    verifyGetValue(result, check, value, RFC_PTR7);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "k\"l": 6} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithDoubleQuotes(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL8);
+    verifyGetValue(result, check, value, RFC_PTR8);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code " ": 7} using
+   * {@code JsonValue getValue(String)}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithSpace(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL9);
+    verifyGetValue(result, check, value, RFC_PTR9);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "m~n": 8} without encoding
+   * using {@code JsonValue getValue(String)}. Passing this test is not
+   * mandatory.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-3">RFC 6901: 3.
+   * Syntax</a>} defines JSON pointer grammar as:<br>
+   * {@code json-pointer    = *( "/" reference-token )}<br>
+   * {@code reference-token = *( unescaped / escaped )}<br>
+   * {@code unescaped       = %x00-2E / %x30-7D / %x7F-10FFFF}<br>
+   * {@code escaped         = "~" ( "0" / "1" )}<br>
+   * Characters {@code '/'} and {@code '~'} are excluded from {@code unescaped}.
+   * But having {@code '~'} outside escape sequence may be acceptable.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithTilde(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR10 + "\" pointer (optional)");
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL10);
+    boolean noError = true;
+    try {
+      final JsonValue out = value.getValue(RFC_PTR10);
+      if (operationFailed(check, out)) {
+        noError = false;
+        System.out.println("    - Pointer \"" + RFC_KEY10
+            + "\" did not return expected value");
+      }
+    } catch (JsonException e) {
+      noError = false;
+      System.out.println("    - Expected exception: " + e.getMessage());
+    }
+    if (noError) {
+      System.out.println(
+          "    - Pointer resolving accepts '~' outside escape sequence");
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "m~n": 8} using
+   * {@code JsonValue getValue(String)}. Character {@code '~'} is encoded as
+   * {@code "~0"} string.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedTilde(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL10);
+    verifyGetValue(result, check, value, RFC_KEY10_ENC);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "o~1p": 9} using
+   * {@code JsonValue getValue(String)}. String {@code "~1"} is encoded as
+   * {@code "~01"} String. Proper encoded sequences transformation is described
+   * in chapter:
+   * {@code "the string '~01' correctly becomes '~1' after transformation"}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedTildeOne(final TestResult result) {
+    final JsonStructure value = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL11);
+    verifyGetValue(result, check, value, RFC_PTR11_ENC);
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for existing numeric indexes of an
+   * array. {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC
+   * 6901: 4. Evaluation</a>} chapter:<br>
+   * If the currently referenced value is a JSON array, the reference token MUST
+   * contain either:
+   * <ul>
+   * <li>characters comprised of digits (see ABNF below; note that leading zeros
+   * are not allowed) that represent an unsigned base-10 integer value, making
+   * the new referenced value the array element with the zero-based index
+   * identified by the token</li>
+   * </ul>
+   */
+  private void testResolveValidNumericIndexInArray(final TestResult result) {
+    System.out.println(
+        " - getValue(String) resolving of pointer containing existing numeric array index");
+    final JsonArray[] arraysIn = new JsonArray[] { createSimpleStringArray5(),
+        createSimpleIntArray5(), createSimpleBoolArray5(),
+        createSimpleObjectArray5() };
+    final JsonValue[] strings = new JsonValue[] { toJsonValue(STR_VALUE_1),
+        toJsonValue(STR_VALUE_2), toJsonValue(STR_VALUE_3),
+        toJsonValue(STR_VALUE_4), toJsonValue(STR_VALUE_5) };
+    final JsonValue[] ints = new JsonValue[] { toJsonValue(INT_VALUE_1),
+        toJsonValue(INT_VALUE_2), toJsonValue(INT_VALUE_3),
+        toJsonValue(INT_VALUE_4), toJsonValue(INT_VALUE_5) };
+    final JsonValue[] bools = new JsonValue[] { toJsonValue(BOOL_FALSE),
+        toJsonValue(BOOL_TRUE), toJsonValue(BOOL_TRUE), toJsonValue(BOOL_FALSE),
+        toJsonValue(BOOL_TRUE) };
+    final JsonValue[] objs = new JsonValue[] { OBJ_VALUE_1, OBJ_VALUE_2,
+        OBJ_VALUE_3, OBJ_VALUE_4, OBJ_VALUE_5 };
+    final JsonValue[][] checks = new JsonValue[][] { strings, ints, bools,
+        objs };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all valid indexes in arrays
+      for (int j = 0; j < 5; j++) {
+        final String path = "/" + Integer.toString(j);
+        try {
+          final JsonValue out = arraysIn[i].getValue(path);
+          if (operationFailed(checks[i][j], out)) {
+            result.fail("getValue(String)", "Failed for \"" + path + "\" path");
+          }
+        } catch (JsonException e) {
+          result.fail("getValue(String)", "Exception: " + e.getMessage());
+        }
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for character {@code '-'} marking the
+   * end of an array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:<br>
+   * If the currently referenced value is a JSON array, the reference token MUST
+   * contain either:
+   * <ul>
+   * <li>exactly the single character "-", making the new referenced value the
+   * (nonexistent) member after the last array element</li>
+   * </ul>
+   * Note that the use of the "-" character to index an array will always result
+   * in such an error condition because by definition it refers to a nonexistent
+   * array element. Thus, applications of JSON Pointer need to specify how that
+   * character is to be handled, if it is to be useful.
+   */
+  private void testResolveMemberAfterLastInArray(final TestResult result) {
+    System.out.println(" - getValue(String) resolving of array \"/-\" pointer");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray(), createSimpleIntArray5(), createBoolArray2(),
+        createSimpleObjectArray5() };
+    for (int i = 0; i < arraysIn.length; i++) {
+      try {
+        arraysIn[i].getValue("/-");
+        result.fail("getValue(String)", "Call of getValue(String) on \"" + "/-"
+            + "\" shall throw JsonException");
+      } catch (JsonException e) {
+        System.out.println("    - Expected exception: " + e.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for invalid index containing non
+   * numeric characters on array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:<br>
+   * {@code array-index = %x30 / ( %x31-39 *(%x30-39) )} grammar rule prohibits
+   * indexes with anything else than sequence of digits. Index {@code '-'} is
+   * being checked in another tests. The only exception is path for whole
+   * document ({@code ""}) which must return the whole array.
+   */
+  private void testResolveNonNumericIndexInArray(final TestResult result) {
+    System.out.println(
+        " - getValue(String) resolving of pointer containing non numeric array index");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray(), createSimpleIntArray5(), createBoolArray2(),
+        createSimpleObjectArray5() };
+    final String[] typeNames = new String[] { "empty", "String", "int",
+        "boolean", "JsonObject" };
+    final String wholeDocument = "";
+    final String[] paths = new String[] { "/", "/1a", "/b4", "/name" };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      try {
+        final JsonValue wholeOut = arraysIn[i].getValue(wholeDocument);
+        if (operationFailed(wholeOut, arraysIn[i])) {
+          result.fail("getValue(String)", "Failed for \"" + wholeDocument
+              + "\" path on " + typeNames[i] + " array");
+        }
+      } catch (JsonException e) {
+        result.fail("getValue(String)", "Failed for \"" + wholeDocument
+            + "\" path on " + typeNames[i] + " array: " + e.getMessage());
+      }
+      for (int j = 0; j < paths.length; j++) {
+        try {
+          final JsonValue out = arraysIn[i].getValue(paths[j]);
+          result.fail("getValue(String)", "Succeeded for \"" + paths[j]
+              + "\" path on " + typeNames[i] + " array");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test helper: Verify {@code JsonValue getValue(String)} for given JSON path.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void verifyGetValue(final TestResult result, final JsonValue check,
+      final JsonStructure value, final String path) {
+    System.out.println(" - getValue(String) resolving of \"" + path + "\" pointer");
+    try {
+      final JsonValue out = value.getValue(path);
+      if (operationFailed(check, out)) {
+        result.fail("getValue(String)", "Failed for \"" + path + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("getValue(String)", "Exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test helper: Verify {@code JsonValue getValue(String)} for given JSON path.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void verifyGetValueFail(final TestResult result,
+      final JsonStructure value, final String path) {
+    System.out.println(
+        " - getValue(String) resolving of invalid \"" + path + "\" pointer");
+    try {
+      value.getValue(path);
+      result.fail("getValue(String)", "Call of getValue(String) on \"" + path
+          + "\" shall throw JsonException");
+    } catch (JsonException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Value.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Value.java
new file mode 100644
index 0000000..29e5179
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonvaluetests/Value.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonvaluetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for {@link JsonValue}.
+ */
+public class Value {
+
+  /**
+   * Creates an instance of JavaScript Object Notation (JSON) compatibility
+   * tests for {@link JsonValue}.
+   */
+  Value() {
+    super();
+  }
+
+  /**
+   * {@link JsonValue} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonValue API methods added in JSON-P 1.1.");
+    System.out.println("JsonValue API methods added in JSON-P 1.1.");
+    testAsJsonObject(result);
+    testAsJsonObjectOnNonObject(result);
+    testAsJsonArray(result);
+    testAsJsonArrayOnNonArray(result);
+    return result;
+  }
+
+  /**
+   * Test {@code JsonObject asJsonObject()} method on {@code JsonObject}
+   * instances.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAsJsonObject(final TestResult result) {
+    System.out.println(" - asJsonObject() on JsonObject instances");
+    final JsonObject[] values = { createEmptyObject(), createSimpleObjectStr(),
+        createSimpleObjectInt(), createSimpleObjectBool(),
+        createSimpleObjectObject(), createCompoundObject() };
+    for (final JsonObject objValue : values) {
+      final JsonValue value = objValue;
+      final JsonObject out = objValue.asJsonObject();
+      if (operationFailed(objValue, out)) {
+        result.fail("asJsonObject()", "Output " + valueToString(out)
+            + " value shall be " + valueToString(objValue));
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonObject asJsonObject()} method on non {@code JsonObject}
+   * instances.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAsJsonObjectOnNonObject(final TestResult result) {
+    System.out.println(" - asJsonObject() on non JsonObject instances");
+    final JsonValue[] values = { createEmptyArrayWithStr(),
+        createEmptyArrayWithInt(), createEmptyArrayWithBool(),
+        createEmptyArrayWithObject(), toJsonValue(STR_VALUE),
+        toJsonValue(INT_VALUE), toJsonValue(LNG_VALUE), toJsonValue(DBL_VALUE),
+        toJsonValue(BIN_VALUE), toJsonValue(BDC_VALUE), toJsonValue(BOOL_VALUE),
+        toJsonValue(null) };
+    for (final JsonValue value : values) {
+      try {
+        value.asJsonObject();
+        result.fail("asJsonObject()",
+            "Call of asJsonObject() on non JsonObject instance shall throw ClassCastException");
+      } catch (ClassCastException ex) {
+        System.out.println("    - Expected exception: " + ex.getMessage());
+      } catch (Throwable t) {
+        result.fail("asJsonObject()",
+            "Call of asJsonObject() on non JsonObject instance shall throw ClassCastException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonObject asJsonArray()} method on {@code JsonArray}
+   * instances.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAsJsonArray(final TestResult result) {
+    System.out.println(" - asJsonArray() on JsonArray instances");
+    final JsonArray[] values = { createEmptyArray(), createEmptyArrayWithStr(),
+        createEmptyArrayWithInt(), createEmptyArrayWithBool(),
+        createEmptyArrayWithObject(), createSimpleStringArray5(),
+        createSimpleIntArray5(), createSimpleBoolArray5(),
+        createSimpleObjectArray5() };
+    for (final JsonArray objValue : values) {
+      final JsonValue value = objValue;
+      final JsonArray out = objValue.asJsonArray();
+      if (operationFailed(objValue, out)) {
+        result.fail("asJsonArray()", "Output " + valueToString(out)
+            + " value shall be " + valueToString(objValue));
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonObject asJsonArray()} method on non {@code JsonArray}
+   * instances.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testAsJsonArrayOnNonArray(final TestResult result) {
+    System.out.println(" - asJsonArray() on non JsonArray instances");
+    final JsonValue[] values = { createSimpleObjectStr(),
+        createSimpleObjectInt(), createSimpleObjectBool(),
+        createSimpleObjectObject(), createCompoundObject(),
+        toJsonValue(STR_VALUE), toJsonValue(INT_VALUE), toJsonValue(LNG_VALUE),
+        toJsonValue(DBL_VALUE), toJsonValue(BIN_VALUE), toJsonValue(BDC_VALUE),
+        toJsonValue(BOOL_VALUE), toJsonValue(null) };
+    for (final JsonValue value : values) {
+      try {
+        value.asJsonArray();
+        result.fail("asJsonArray()",
+            "Call of asJsonArray() on non JsonArray instance shall throw ClassCastException");
+      } catch (ClassCastException ex) {
+        System.out.println("    - Expected exception: " + ex.getMessage());
+      } catch (Throwable t) {
+        result.fail("asJsonArray()",
+            "Call of asJsonArray() on non JsonArray instance shall throw ClassCastException, not "
+                + t.getClass().getSimpleName());
+      }
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwriterfactorytests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwriterfactorytests/ClientTests.java
new file mode 100644
index 0000000..875bbe1
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwriterfactorytests/ClientTests.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+ * $Id$
+ */
+package jakarta.jsonp.tck.api.jsonwriterfactorytests;
+
+import jakarta.json.*;
+import jakarta.json.stream.*;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+import java.util.Properties;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ArrayList;
+
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonWriterFactoryTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:110; JSONP:JAVADOC:414; JSONP:JAVADOC:422;
+   * JSONP:JAVADOC:452; JSONP:JAVADOC:463;
+   * 
+   * @test_Strategy: Tests the JsonWriterFactory API.
+   *
+   * JsonWriterFactory writerFactory = Json.createWriterFactory(Map<String, ?>);
+   * JsonWriter writer1 = writerFactory.createWriter(Writer) JsonWriter writer2
+   * = writerFactory.createWriter(Writer)
+   */
+  @Test
+  public void jsonWriterFactoryTest1() throws Fault {
+    boolean pass = true;
+    JsonWriter writer1 = null;
+    JsonWriter writer2 = null;
+    String expString = "{}";
+    String actString;
+    JsonObject jsonObject = Json.createReader(new StringReader(expString))
+        .readObject();
+    try {
+      System.out.println("Create JsonWriterFactory with Map<String, ?> with EMPTY config");
+      JsonWriterFactory writerFactory = Json
+          .createWriterFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = writerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+      System.out.println("--------------------------------------------------");
+      System.out.println("TEST CASE [JsonWriterFactory.createWriter(Writer)]");
+      System.out.println("--------------------------------------------------");
+      System.out.println("Create 1st JsonWriter using JsonWriterFactory");
+      Writer sWriter1 = new StringWriter();
+      writer1 = writerFactory.createWriter(sWriter1);
+      if (writer1 == null) {
+        System.err.println("WriterFactory failed to create writer1");
+        pass = false;
+      } else {
+        writer1.writeObject(jsonObject);
+        writer1.close();
+      }
+      System.out.println("sWriter1=" + sWriter1.toString());
+      actString = JSONP_Util.removeWhitespace(sWriter1.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println("Create 2nd JsonWriter using JsonWriterFactory");
+      Writer sWriter2 = new StringWriter();
+      writer2 = writerFactory.createWriter(sWriter2);
+      if (writer2 == null) {
+        System.err.println("WriterFactory failed to create writer2");
+        pass = false;
+      } else {
+        writer2.writeObject(jsonObject);
+        writer2.close();
+      }
+      System.out.println("sWriter2=" + sWriter2.toString());
+      actString = JSONP_Util.removeWhitespace(sWriter2.toString());
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonWriterFactoryTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterFactoryTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterFactoryTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:110; JSONP:JAVADOC:414; JSONP:JAVADOC:424;
+   * JSONP:JAVADOC:452; JSONP:JAVADOC:463;
+   * 
+   * @test_Strategy: Tests the JsonWriterFactory API.
+   *
+   * JsonWriterFactory writerFactory = Json.createWriterFactory(Map<String,?>);
+   * JsonWriter writer1 = writerFactory.createWriter(OutputStream, Charset)
+   * JsonWriter writer2 = writerFactory.createWriter(OutputStream, Charset)
+   *
+   * Create writer with both UTF-8 and UTF-16BE.
+   */
+  @Test
+  public void jsonWriterFactoryTest2() throws Fault {
+    boolean pass = true;
+    JsonWriter writer1 = null;
+    JsonWriter writer2 = null;
+    String expString = "{}";
+    String actString;
+    JsonObject jsonObject = Json.createReader(new StringReader(expString))
+        .readObject();
+    try {
+      System.out.println("Create JsonWriterFactory with Map<String, ?> with EMPTY config");
+      JsonWriterFactory writerFactory = Json
+          .createWriterFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = writerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println(
+          "-----------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [JsonWriterFactory.createWriter(OutputStream, Charset)]");
+      System.out.println(
+          "-----------------------------------------------------------------");
+      System.out.println(
+          "Create 1st JsonWriter using JsonWriterFactory with UTF-8 encoding");
+      ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+      writer1 = writerFactory.createWriter(baos1, JSONP_Util.UTF_8);
+      if (writer1 == null) {
+        System.err.println("WriterFactory failed to create writer1");
+        pass = false;
+      } else {
+        writer1.writeObject(jsonObject);
+        writer1.close();
+      }
+      System.out.println("baos1=" + baos1.toString("UTF-8"));
+      actString = JSONP_Util.removeWhitespace(baos1.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println(
+          "Create 2nd JsonWriter using JsonWriterFactory with UTF-8 encoding");
+      ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+      writer2 = writerFactory.createWriter(baos2, JSONP_Util.UTF_8);
+      if (writer2 == null) {
+        System.err.println("WriterFactory failed to create writer2");
+        pass = false;
+      } else {
+        writer2.writeObject(jsonObject);
+        writer2.close();
+      }
+      System.out.println("baos2=" + baos2.toString("UTF-8"));
+      actString = JSONP_Util.removeWhitespace(baos2.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonWriterFactoryTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterFactoryTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterFactoryTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:110; JSONP:JAVADOC:414; JSONP:JAVADOC:423;
+   * JSONP:JAVADOC:452; JSONP:JAVADOC:463;
+   * 
+   * @test_Strategy: Tests the JsonWriterFactory API.
+   *
+   * JsonWriterFactory writerFactory = Json.createWriterFactory(Map<String,?>);
+   * JsonWriter writer1 = writerFactory.createWriter(OutputStream) JsonWriter
+   * writer2 = writerFactory.createWriter(OutputStream)
+   */
+  @Test
+  public void jsonWriterFactoryTest3() throws Fault {
+    boolean pass = true;
+    JsonWriter writer1 = null;
+    JsonWriter writer2 = null;
+    String expString = "{}";
+    String actString;
+    JsonObject jsonObject = Json.createReader(new StringReader(expString))
+        .readObject();
+    try {
+      System.out.println("Create JsonWriterFactory with Map<String, ?> with EMPTY config");
+      JsonWriterFactory writerFactory = Json
+          .createWriterFactory(JSONP_Util.getEmptyConfig());
+      System.out.println("Checking factory configuration properties");
+      Map<String, ?> config = writerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("--------------------------------------------------------");
+      System.out.println("TEST CASE [JsonWriterFactory.createWriter(OutputStream)]");
+      System.out.println("--------------------------------------------------------");
+      System.out.println("Create 1st JsonWriter using JsonWriterFactory");
+      ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+      writer1 = writerFactory.createWriter(baos1);
+      if (writer1 == null) {
+        System.err.println("WriterFactory failed to create writer1");
+        pass = false;
+      } else {
+        writer1.writeObject(jsonObject);
+        writer1.close();
+      }
+      System.out.println("baos1=" + baos1.toString("UTF-8"));
+      actString = JSONP_Util.removeWhitespace(baos1.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+      System.out.println("Create 2nd JsonWriter using JsonWriterFactory");
+      ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+      writer2 = writerFactory.createWriter(baos2);
+      if (writer2 == null) {
+        System.err.println("WriterFactory failed to create writer2");
+        pass = false;
+      } else {
+        writer2.writeObject(jsonObject);
+        writer2.close();
+      }
+      System.out.println("baos2=" + baos2.toString("UTF-8"));
+      actString = JSONP_Util.removeWhitespace(baos2.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expString, actString))
+        pass = false;
+
+    } catch (Exception e) {
+      throw new Fault("jsonWriterFactoryTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterFactoryTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterFactoryTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:452; JSONP:JAVADOC:463;
+   * 
+   * @test_Strategy: Tests the JsonWriterFactory API.
+   *
+   * JsonWriterFactory writerFactory = Json.createWriterFactory(Map<String, ?>);
+   * Map<String, ?> config = JsonWriterFactory.getConfigInUse();
+   *
+   * Test for the following 3 scenarios: 1) no supported provider property
+   * (empty config) 2) supported provider property 3) supported and non
+   * supported provider property
+   */
+  @Test
+  public void jsonWriterFactoryTest4() throws Fault {
+    boolean pass = true;
+    JsonWriterFactory writerFactory;
+    Map<String, ?> config;
+    try {
+      System.out.println("----------------------------------------------");
+      System.out.println("Test scenario1: no supported provider property");
+      System.out.println("----------------------------------------------");
+      System.out.println("Create JsonWriterFactory with Map<String, ?> with EMPTY config");
+      writerFactory = Json.createWriterFactory(JSONP_Util.getEmptyConfig());
+      config = writerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-------------------------------------------");
+      System.out.println("Test scenario2: supported provider property");
+      System.out.println("-------------------------------------------");
+      System.out.println("Create JsonWriterFactory with Map<String, ?> with FOO config");
+      writerFactory = Json.createWriterFactory(JSONP_Util.getFooConfig());
+      config = writerFactory.getConfigInUse();
+      String[] props = { JsonGenerator.PRETTY_PRINTING, };
+      if (!JSONP_Util.doConfigCheck(config, 0))
+        pass = false;
+
+      System.out.println("-------------------------------------------------------------");
+      System.out.println("Test scenario3: supported and non supported provider property");
+      System.out.println("-------------------------------------------------------------");
+      System.out.println("Create JsonGeneratorFactory with Map<String, ?> with all config");
+      writerFactory = Json.createWriterFactory(JSONP_Util.getAllConfig());
+      config = writerFactory.getConfigInUse();
+      if (!JSONP_Util.doConfigCheck(config, 1, props))
+        pass = false;
+    } catch (Exception e) {
+      throw new Fault("jsonWriterFactoryTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterFactoryTest4 Failed");
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/ClientTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/ClientTests.java
new file mode 100644
index 0000000..19bed5b
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/ClientTests.java
@@ -0,0 +1,1108 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonwritertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.common.*;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import java.io.*;
+import java.util.*;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import jakarta.json.*;
+
+// $Id$
+@RunWith(Arquillian.class)
+public class ClientTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, ClientTests.class.getPackage().getName());
+    }
+  /* Tests */
+
+  /*
+   * @testName: jsonWriterTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:110;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonObject.
+   * Comparison is done by reading the JsonWriter output using JsonReader and
+   * recreating the JsonObject and than performing a JsonObject comparison for
+   * equality.
+   *
+   * Tests using API methods: Json.createWriter(Writer) and
+   * writer.writeObject(JsonObject)
+   *
+   */
+  @Test
+  public void jsonWriterTest1() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeObject(myJsonObject1);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+
+      System.out.println(
+          "Read the JsonObject back into 'myJsonObject2' using a JsonReader");
+      JsonReader reader = Json.createReader(new StringReader(contents));
+      JsonObject myJsonObject2 = (JsonObject) reader.read();
+
+      System.out.println("Compare myJsonObject1 and myJsonObject2 for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(myJsonObject1, myJsonObject2);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:187; JSONP:JAVADOC:110;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonObject.
+   * Comparison is done by comparing the expected JsonObject text output with
+   * the actual JsonObject text output from the JsonWriter for equality.
+   *
+   * Tests using API methods: Json.createWriter(OutputStream) and
+   * writer.writeObject(JsonObject)
+   *
+   */
+  @Test
+  public void jsonWriterTest2() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriter(baos);
+      writer.writeObject(myJsonObject1);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonObjectText = baos.toString("UTF-8");
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonObjectText);
+
+      System.out.println(
+          "Compare expected JsonObject text with actual JsonObject text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONOBJECT_TEXT, actJsonObjectText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest3
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:107;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonArray.
+   * Comparison is done by reading the JsonWriter output using JsonReader and
+   * recreating the JsonArray and than performing a JsonArray comparison for
+   * equality.
+   *
+   * Tests using API methods: Json.createWriter(Writer) and
+   * writer.writeArray(JsonArray)
+   *
+   */
+  @Test
+  public void jsonWriterTest3() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.writeArray(myJsonArray1);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+
+      System.out.println("Read the JsonArray back into 'myJsonArray2' using a JsonReader");
+      JsonArray myJsonArray2;
+      try (JsonReader reader = Json.createReader(new StringReader(contents))) {
+        myJsonArray2 = (JsonArray) reader.read();
+        System.out.println("Close JsonReader");
+      }
+
+      System.out.println("Compare myJsonArray1 and myJsonArray2 for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(myJsonArray1, myJsonArray2);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest3 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest3 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest4
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:187; JSONP:JAVADOC:107;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonArray.
+   * Comparison is done by comparing the expected JsonArray text output with the
+   * actual JsonArray text output from the JsonWriter for equality.
+   *
+   * Tests using API methods: Json.createWriter(OutputStream) and
+   * writer.writeArray(JsonArray)
+   *
+   */
+  @Test
+  public void jsonWriterTest4() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriter(baos);
+      writer.writeArray(myJsonArray1);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonArrayText = baos.toString("UTF-8");
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonArrayText);
+
+      System.out.println(
+          "Compare expected JsonArray text with actual JsonArray text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONARRAY_TEXT, actJsonArrayText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest4 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest4 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest5
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:424; JSONP:JAVADOC:110;
+   * JSONP:JAVADOC:452;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonObject.
+   * Comparison is done by comparing the expected JsonObject text output with
+   * the actual JsonObject text output from the JsonWriter for equality.
+   *
+   * Tests using API methods:
+   * Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset)
+   * writer.writeObject(JsonObject)
+   *
+   * For encoding use UTF-16BE.
+   */
+  @Test
+  public void jsonWriterTest5() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_16BE);
+      writer.writeObject(myJsonObject1);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonObjectText = JSONP_Util
+          .removeWhitespace(baos.toString("UTF-16BE"));
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonObjectText);
+
+      System.out.println(
+          "Compare expected JsonObject text with actual JsonObject text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONOBJECT_TEXT, actJsonObjectText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest5 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest5 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest6
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:424; JSONP:JAVADOC:107;
+   * JSONP:JAVADOC:452;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonArray.
+   * Comparison is done by comparing the expected JsonArray text output with the
+   * actual JsonArray text output from the JsonWriter for equality.
+   *
+   * Tests using API methods:
+   * Json.createWriterFactory.createWriter(OutputStream, Charset)
+   * writer.writeArray(JsonArray)
+   *
+   * For encoding use UTF-8.
+   */
+  @Test
+  public void jsonWriterTest6() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create a configuration with PRETT_PRINTING enabled.");
+      Map<String, ?> config = JSONP_Util.getPrettyPrintingConfig();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(config).createWriter(baos,
+          JSONP_Util.UTF_8);
+      writer.writeArray(myJsonArray1);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonArrayText = JSONP_Util
+          .removeWhitespace(baos.toString("UTF-8"));
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonArrayText);
+
+      System.out.println(
+          "Compare expected JsonArray text with actual JsonArray text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONARRAY_TEXT, actJsonArrayText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest6 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest6 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest7
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:191;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonObject.
+   * Comparison is done by reading the JsonWriter output using JsonReader and
+   * recreating the JsonObject and than performing a JsonObject comparison for
+   * equality.
+   *
+   * Tests using API methods: Json.createWriter(Writer) and
+   * writer.write(JsonStructure)
+   *
+   */
+  @Test
+  public void jsonWriterTest7() throws Fault {
+    boolean pass = true;
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.write(myJsonObject1);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+
+      System.out.println(
+          "Read the JsonObject back into 'myJsonObject2' using a JsonReader");
+      JsonReader reader = Json.createReader(new StringReader(contents));
+      JsonObject myJsonObject2 = (JsonObject) reader.read();
+
+      System.out.println("Compare myJsonObject1 and myJsonObject2 for equality");
+      pass = JSONP_Util.assertEqualsJsonObjects(myJsonObject1, myJsonObject2);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest7 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest7 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterTest8
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:191;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonArray.
+   * Comparison is done by reading the JsonWriter output using JsonReader and
+   * recreating the JsonArray and than performing a JsonArray comparison for
+   * equality.
+   *
+   * Tests using API methods: Json.createWriter(Writer) and
+   * writer.write(JsonStructure)
+   *
+   */
+  @Test
+  public void jsonWriterTest8() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      StringWriter sWriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriter(sWriter)) {
+        writer.write(myJsonArray1);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String contents = sWriter.toString();
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + contents);
+
+      System.out.println("Read the JsonArray back into 'myJsonArray2' using a JsonReader");
+      JsonReader reader = Json.createReader(new StringReader(contents));
+      JsonArray myJsonArray2 = (JsonArray) reader.read();
+
+      System.out.println("Compare myJsonArray1 and myJsonArray2 for equality");
+      pass = JSONP_Util.assertEqualsJsonArrays(myJsonArray1, myJsonArray2);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterTest8 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterTest8 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterUTFEncodedTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:110; JSONP:JAVADOC:423; JSONP:JAVADOC:452;
+   * 
+   * @test_Strategy: Tests various JsonWriter API's to create a JsonObject.
+   *
+   * The output is written to an OutputStream using all supported UTF encodings
+   * and read back as a string and filtered to remove whitespace and is compared
+   * against an expected string. The following UTF encodings are tested:
+   *
+   * UTF8 UTF16 UTF16LE UTF16BE UTF32LE UTF32BE
+   *
+   * { "object":{"string":"string","number":1,"true":true,"false":false,"null":
+   * null}, "array":["string", 1, true, false, null] }
+   */
+  @Test
+  public void jsonWriterUTFEncodedTests() throws Fault {
+    boolean pass = true;
+    System.out.println(
+        "Create expected JSON text with no whitespace for use in comparsion");
+    String expJson = "{\"object\":{\"string\":\"string\",\"number\":1,\"true\":true,\"false\":false,\"null\":null},\"array\":[\"string\",1,true,false,null]}";
+    try {
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-8]");
+      System.out.println(
+          "-----------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-8 encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_8);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-8"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-8 encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-8"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing generation to UTF-8 encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-16]");
+      System.out.println(
+          "------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-16 encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_16);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16 encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Exception occurred testing generation to UTF-16 encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-16LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-16LE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_16LE);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16LE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16LE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16LE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-16LE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-16BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-16BE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_16BE);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-16BE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-16BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-16BE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-16BE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-32LE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-32LE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_32LE);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-32LE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-32LE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-32LE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-32LE encoding: " + e);
+    }
+    try {
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println(
+          "TEST CASE [Json.createWriterFactory(Map<String,?>).createWriter(OutputStream, Charset) as UTF-32BE]");
+      System.out.println(
+          "--------------------------------------------------------------------------------------------------");
+      System.out.println("Create JsonWriter using UTF-32BE encoding");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(JSONP_Util.getEmptyConfig())
+          .createWriter(baos, JSONP_Util.UTF_32BE);
+      JSONP_Util.writeJsonObjectFromString(writer, expJson);
+
+      // Dump JsonText output
+      System.out.println("Generated Output=" + baos.toString("UTF-32BE"));
+
+      // Do comparison
+      System.out.println(
+          "Read the JSON text back from OutputStream using UTF-32BE encoding removing whitespace");
+      String actJson = JSONP_Util.removeWhitespace(baos.toString("UTF-32BE"));
+      if (!JSONP_Util.assertEqualsJsonText(expJson, actJson))
+        pass = false;
+
+    } catch (Exception e) {
+      pass = false;
+      System.err.println(
+          "Exception occurred testing generation to UTF-32BE encoding: " + e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterUTFEncodedTests Failed");
+  }
+
+  /*
+   * @testName: jsonWriterWithConfigTest1
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:422; JSONP:JAVADOC:110;
+   * JSONP:JAVADOC:452;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonObject.
+   * Comparison is done by comparing the expected JsonObject text output with
+   * the actual JsonObject text output from the JsonWriter for equality.
+   *
+   * Tests using API methods:
+   * Json.createWriterFactory(Map<String,?>).createWriter(Writer)
+   * writer.writeObject(JsonObject)
+   *
+   */
+  @Test
+  public void jsonWriterWithConfigTest1() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create a configuration with PRETT_PRINTING enabled.");
+      Map<String, ?> config = JSONP_Util.getPrettyPrintingConfig();
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject1 = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Write the JsonObject 'myJsonObject1' out to a JsonWriter");
+      StringWriter swriter = new StringWriter();
+      try (JsonWriter writer = Json.createWriterFactory(config)
+          .createWriter(swriter)) {
+        writer.writeObject(myJsonObject1);
+        System.out.println("Close JsonWriter");
+      }
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonObjectText = JSONP_Util
+          .removeWhitespace(swriter.toString());
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonObjectText);
+
+      System.out.println(
+          "Compare expected JsonObject text with actual JsonObject text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONOBJECT_TEXT, actJsonObjectText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterWithConfigTest1 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterWithConfigTest1 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterWithConfigTest2
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:423; JSONP:JAVADOC:107;
+   * 
+   * @test_Strategy: Tests JsonWriter API's for writing out a JsonArray.
+   * Comparison is done by comparing the expected JsonArray text output with the
+   * actual JsonArray text output from the JsonWriter for equality.
+   *
+   * Tests using API methods:
+   * Json.createWriterFactory(Map<String,?>).creatWriter(OutputStream)
+   * writer.writeArray(JsonArray)
+   *
+   */
+  @Test
+  public void jsonWriterWithConfigTest2() throws Fault {
+    boolean pass = true;
+    try {
+
+      System.out.println("Create a configuration with PRETT_PRINTING enabled.");
+      Map<String, ?> config = JSONP_Util.getPrettyPrintingConfig();
+
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray1 = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Write the JsonArray 'myJsonArray1' out to a JsonWriter");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      JsonWriter writer = Json.createWriterFactory(config).createWriter(baos);
+      writer.writeArray(myJsonArray1);
+      System.out.println("Close JsonWriter");
+      baos.close();
+      writer.close();
+
+      System.out.println("Save contents of the JsonWriter as a String");
+      String actJsonArrayText = JSONP_Util
+          .removeWhitespace(baos.toString("UTF-8"));
+
+      System.out.println("Dump contents of JsonWriter as a String");
+      System.out.println("JsonWriterContents=" + actJsonArrayText);
+
+      System.out.println(
+          "Compare expected JsonArray text with actual JsonArray text for equality");
+      pass = JSONP_Util.assertEqualsJsonText(
+          JSONP_Util.EXPECTED_SAMPLEJSONARRAY_TEXT, actJsonArrayText);
+    } catch (Exception e) {
+      throw new Fault("jsonWriterWithConfigTest2 Failed: ", e);
+    }
+    if (!pass)
+      throw new Fault("jsonWriterWithConfigTest2 Failed");
+  }
+
+  /*
+   * @testName: jsonWriterExceptionTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:105; JSONP:JAVADOC:106; JSONP:JAVADOC:109;
+   * JSONP:JAVADOC:112; JSONP:JAVADOC:222;
+   * 
+   * @test_Strategy: Test for JsonWriter exception test conditions. o
+   * IllegalStateException
+   *
+   */
+  @Test
+  public void jsonWriterExceptionTests() throws Fault {
+    boolean pass = true;
+    JsonWriter writer = null;
+
+    // IllegalStateException if writer.close() already called before
+    // writer.writeArray(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create JsonWriter, write something and close it");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeArray(jsonArray);
+      writer.close();
+
+      System.out.println(
+          "IllegalStateException if writer.close() already called before writer.writeArray(JsonArray)");
+      writer.writeArray(jsonArray);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if writer.writeArray() called after
+    // writer.writeArray(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create JsonWriter and write out array");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeArray(jsonArray);
+
+      System.out.println(
+          "IllegalStateException if writer.writeArray(JsonArray) called after writer.writeArray(JsonArray)");
+      writer.writeArray(jsonArray);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    // IllegalStateException if writer.writeObject() called after
+    // writer.writeArray(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject jsonObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create JsonWriter and write out array");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeArray(jsonArray);
+
+      System.out.println(
+          "IllegalStateException if writer.writeObject(JsonObject) called after writer.writeArray(JsonArray)");
+      writer.writeObject(jsonObject);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    // IllegalStateException if writer.close() already called before
+    // writer.writeObject(JsonArray)
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject jsonObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create JsonWriter, write something and close it");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeObject(jsonObject);
+      writer.close();
+
+      System.out.println(
+          "IllegalStateException if writer.close() already called before writer.writeObject(JsonObject)");
+      writer.writeObject(jsonObject);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if writer.writeObject() called after
+    // writer.writeObject(JsonObject)
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject jsonObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create JsonWriter and write out object");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeObject(jsonObject);
+
+      System.out.println(
+          "IllegalStateException if writer.writeObject(JsonObject) called after writer.writeObject(JsonObject)");
+      writer.writeObject(jsonObject);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    // IllegalStateException if writer.writeArray() called after
+    // writer.writeObject(JsonObject)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject jsonObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create JsonWriter and write out object");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeObject(jsonObject);
+
+      System.out.println(
+          "IllegalStateException if writer.writeArray(JsonArray) called after writer.writeObject(JsonObject)");
+      writer.writeArray(jsonArray);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    // IllegalStateException if writer.close() already called before
+    // writer.write(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create JsonWriter, write something and close it");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.write(jsonArray);
+      writer.close();
+
+      System.out.println(
+          "IllegalStateException if writer.close() already called before writer.write(JsonArray)");
+      writer.write(jsonArray);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // IllegalStateException if writer.write(JsonArray) called after
+    // writer.writeArray(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray jsonArray = JSONP_Util.createSampleJsonArray();
+
+      System.out.println("Create JsonWriter and write out array");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeArray(jsonArray);
+
+      System.out.println(
+          "IllegalStateException if writer.write(JsonArray) called after writer.writeArray(JsonArray)");
+      writer.write(jsonArray);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    // IllegalStateException if writer.write(JsonObject) called after
+    // writer.writeJsonObject(JsonObject)
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject jsonObject = JSONP_Util.createSampleJsonObject();
+
+      System.out.println("Create JsonWriter and write out object");
+      StringWriter sWriter = new StringWriter();
+      writer = Json.createWriter(sWriter);
+      writer.writeObject(jsonObject);
+
+      System.out.println(
+          "IllegalStateException if writer.write(JsonObject) called after writer.writeObject(JsonObject)");
+      writer.write(jsonObject);
+      pass = false;
+      System.err.println("Failed to throw IllegalStateException");
+    } catch (IllegalStateException e) {
+      System.out.println("Got expected IllegalStateException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    } finally {
+      if (writer != null)
+        writer.close();
+    }
+
+    if (!pass)
+      throw new Fault("jsonWriterExceptionTests Failed");
+  }
+
+  /*
+   * @testName: jsonWriterIOErrorTests
+   * 
+   * @assertion_ids: JSONP:JAVADOC:108; JSONP:JAVADOC:111; JSONP:JAVADOC:221;
+   * JSONP:JAVADOC:414;
+   * 
+   * @test_Strategy: Tests for JsonException for testable i/o errors.
+   *
+   */
+  @Test
+  public void jsonWriterIOErrorTests() throws Fault {
+    boolean pass = true;
+
+    // Trip JsonException if there is an i/o error on JsonWriter.close()
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject = JSONP_Util.createSampleJsonObject();
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonWriter.close().");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      try (JsonWriter writer = Json.createWriter(mbw)) {
+        writer.writeObject(myJsonObject);
+        mbw.setThrowIOException(true);
+        System.out.println("Calling JsonWriter.close()");
+      }
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on
+    // JsonWriter.writeObject(JsonObject)
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject = JSONP_Util.createSampleJsonObject();
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonWriter.writeObject(JsonObject).");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      try (JsonWriter writer = Json.createWriter(mbw)) {
+        mbw.setThrowIOException(true);
+        System.out.println("Calling JsonWriter.writeObject(JsonObject)");
+        writer.writeObject(myJsonObject);
+      }
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on
+    // JsonWriter.writeArray(JsonArray)
+    try {
+      System.out.println("Create sample JsonArray for testing");
+      JsonArray myJsonArray = JSONP_Util.createSampleJsonArray();
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonWriter.writeArray(JsonArray).");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      try (JsonWriter writer = Json.createWriter(mbw)) {
+        mbw.setThrowIOException(true);
+        System.out.println("Calling JsonWriter.writeArray(JsonArray)");
+        writer.writeArray(myJsonArray);
+      }
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    // Trip JsonException if there is an i/o error on
+    // JsonWriter.write(JsonStructure)
+    try {
+      System.out.println("Create sample JsonObject for testing");
+      JsonObject myJsonObject = JSONP_Util.createSampleJsonObject();
+      System.out.println(
+          "Trip JsonException if there is an i/o error on JsonWriter.write(JsonStructure).");
+      MyBufferedWriter mbw = new MyBufferedWriter(new StringWriter());
+      try (JsonWriter writer = Json.createWriter(mbw)) {
+        mbw.setThrowIOException(true);
+        System.out.println("Calling JsonWriter.write(JsonStructure)");
+        writer.write(myJsonObject);
+      }
+      System.err.println("Did not get expected JsonException");
+      pass = false;
+    } catch (JsonException e) {
+      System.out.println("Caught expected JsonException");
+    } catch (Exception e) {
+      pass = false;
+      System.err.println("Caught unexpected exception: " + e);
+    }
+
+    if (!pass)
+      throw new Fault("jsonWriterIOErrorTests Failed");
+  }
+
+  /*
+   * @testName: jsonWriter11Test
+   * 
+   * @assertion_ids: JSONP:JAVADOC:650; JSONP:JAVADOC:583; JSONP:JAVADOC:584;
+   * JSONP:JAVADOC:585; JSONP:JAVADOC:586; JSONP:JAVADOC:587; JSONP:JAVADOC:588;
+   * JSONP:JAVADOC:662; JSONP:JAVADOC:663; JSONP:JAVADOC:664; JSONP:JAVADOC:665;
+   * JSONP:JAVADOC:666; JSONP:JAVADOC:667;
+   * 
+   * @test_Strategy: Tests JsonWriter API methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonWriter11Test() throws Fault {
+    Writer writerTest = new Writer();
+    final TestResult result = writerTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/Writer.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/Writer.java
new file mode 100644
index 0000000..8de8832
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/jsonwritertests/Writer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.jsonwritertests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.SimpleValues;
+import jakarta.jsonp.tck.api.common.TestResult;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import jakarta.json.Json;
+import jakarta.json.JsonException;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonWriter;
+import jakarta.json.stream.JsonParser;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests for {@link JsonWriter}.
+ */
+public class Writer {
+
+  /** Tests input data. */
+  private static final Object[] VALUES = new Object[] { OBJ_VALUE, // write(JsonValue)
+                                                                   // for
+                                                                   // JsonObject
+      createEmptyArrayWithStr(), // write(JsonValue) for simple JsonArray
+      STR_VALUE, // write(JsonValue) for String
+      INT_VALUE, // write(JsonValue) for int
+      LNG_VALUE, // write(JsonValue) for long
+      DBL_VALUE, // write(JsonValue) for double
+      BIN_VALUE, // write(JsonValue) for BigInteger
+      BDC_VALUE, // write(JsonValue) for BigDecimal
+      BOOL_VALUE, // write(JsonValue) for boolean
+      null // write(JsonValue) for null
+  };
+
+  /**
+   * Creates an instance of JavaScript Object Notation (JSON) compatibility
+   * tests for {@link JsonWriter}.
+   */
+  Writer() {
+    super();
+  }
+
+  /**
+   * {@link JsonWriter} API methods added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonWriter API methods added in JSON-P 1.1.");
+    System.out.println("JsonWriter API methods added in JSON-P 1.1.");
+    testWriteValue(result);
+    testDoubleWriteValue(result);
+    testIOExceptionOnWriteValue(result);
+    return result;
+  }
+
+  /**
+   * Test {@code void write(JsonValue)} method on all child types of
+   * {@code JsonValue}.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testWriteValue(final TestResult result) {
+    for (Object value : VALUES) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(" - write(JsonValue) for " + typeName + " as an argument");
+      final JsonValue jsonValue = SimpleValues.toJsonValue(value);
+      final StringWriter strWriter = new StringWriter();
+      try (final JsonWriter writer = Json.createWriter(strWriter)) {
+        writer.write(jsonValue);
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("write(JsonValue)",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+      final String data = strWriter.toString();
+      System.out.println("    - Data: " + data);
+      final JsonParser parser = Json.createParser(new StringReader(data));
+      parser.next();
+      final JsonValue outValue = parser.getValue();
+      if (operationFailed(jsonValue, outValue)) {
+        result.fail("write(JsonValue)",
+            "Writer output " + valueToString(outValue) + " value shall be "
+                + valueToString(jsonValue));
+      }
+    }
+  }
+
+  /**
+   * Test {@code void write(JsonValue)} method with duplicated {@code JsonValue}
+   * write call. Second call is expected to throw {@code IllegalStateException}
+   * exception.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testDoubleWriteValue(final TestResult result) {
+    for (Object value : VALUES) {
+      final String typeName = JsonValueType.getType(value).name();
+      System.out.println(
+          " - duplicate write(JsonValue) for " + typeName + " as an argument");
+      final JsonValue jsonValue = SimpleValues.toJsonValue(value);
+      final StringWriter strWriter = new StringWriter();
+      try (final JsonWriter writer = Json.createWriter(strWriter)) {
+        // 1st attempt to write the data shall pass
+        writer.write(jsonValue);
+        try {
+          // 2nd attempt to write the data shall throw IllegalStateException
+          writer.write(jsonValue);
+          result.fail("write(JsonValue)",
+              "Duplicate call of write(JsonValue) shall throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+          System.out.println("    - Expected exception: " + ex.getMessage());
+        } catch (Throwable t) {
+          result.fail("write(JsonValue)",
+              "Duplicate call of write(JsonValue) shall throw IllegalStateException, not "
+                  + t.getClass().getSimpleName());
+        }
+      } catch (JsonException ex) {
+        System.out.println("Caught JsonException: " + ex.getLocalizedMessage());
+        result.fail("write(JsonValue)",
+            "Caught JsonException: " + ex.getLocalizedMessage());
+      }
+    }
+  }
+
+  /**
+   * Test {@code void write(JsonValue)} method with write call that causes
+   * IOException. IOException shall be encapsulated in JsonException.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  @SuppressWarnings("ConvertToTryWithResources")
+  private void testIOExceptionOnWriteValue(final TestResult result) {
+    System.out.println(" - write(JsonValue) into already closed file writer");
+    final JsonValue jsonValue = SimpleValues.toJsonValue(DEF_VALUE);
+    File temp = null;
+    JsonWriter writer;
+    // Close writer before calling write method.
+    try {
+      temp = File.createTempFile("testIOExceptionOnWriteValue", ".txt");
+      System.out.println("    - Temporary file: " + temp.getAbsolutePath());
+      final FileWriter fileWriter = new FileWriter(temp);
+      writer = Json.createWriter(fileWriter);
+      fileWriter.close();
+    } catch (IOException ex) {
+      System.out.println("Caught IOException: " + ex.getLocalizedMessage());
+      result.fail("write(JsonValue)",
+          "Caught IOException: " + ex.getLocalizedMessage());
+      return;
+    } finally {
+      if (temp != null) {
+        temp.delete();
+      }
+    }
+    try {
+      writer.write(jsonValue);
+      result.fail("write(JsonValue)",
+          "Call of write(JsonValue) on already closed file writer shall throw JsonException");
+    } catch (JsonException ex) {
+      System.out.println("    - Expected exception: " + ex.getMessage());
+    } catch (Throwable t) {
+      result.fail("write(JsonValue)",
+          "Call of write(JsonValue) on already closed file writer shall throw JsonException, not "
+              + t.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeAddValue.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeAddValue.java
new file mode 100644
index 0000000..13d5b78
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeAddValue.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonObject;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * Checks scenario described in
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+ * Introduction</a>}: If the provided merge patch contains members that do not
+ * appear within the target, those members are added.
+ */
+public class MergeAddValue extends MergeCommon {
+
+  /**
+   * Creates an instance of RFC 7396 value adding test.
+   */
+  MergeAddValue() {
+    super();
+  }
+
+  /**
+   * Test RFC 7396: Adding non existing values. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 7396: Add non existing values");
+    System.out.println("Testing RFC 7396: Add non existing values");
+    testStringOnEmptyObject(result);
+    testStringOnsimpleObject(result);
+    testIntOnEmptyObject(result);
+    testIntOnsimpleObject(result);
+    testBoolOnEmptyObject(result);
+    testBoolOnsimpleObject(result);
+    testObjectOnEmptyObject(result);
+    testObjectOnsimpleObject(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject patch = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectStr();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnsimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectWithStr();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject patch = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectInt();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnsimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectWithInt();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject patch = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectBool();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnsimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectWithBool();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonObject} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject patch = createSimpleObjectObject();
+    final JsonObject check = createSimpleObjectObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonObject} on compound JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testObjectOnsimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on compound JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonObject patch = createSimpleObjectObject();
+    final JsonObject check = createCompoundObjectWithObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeCommon.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeCommon.java
new file mode 100644
index 0000000..1fc3f03
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeCommon.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests.<br>
+ */
+public abstract class MergeCommon {
+
+  /** Message content: "MERGE" operation. */
+  private static final String MERGE_STR = "MERGE";
+
+  /** Message content: "DIFF" operation. */
+  private static final String DIFF_STR = "DIFF";
+
+  /** Message content template for test failure log message: patch. */
+  private static final String TEST_FAIL_PATCH = "Patch ";
+
+  /** Message content template for test failure log message: failed on. */
+  private static final String TEST_FAIL_ON = " failed on ";
+
+  /** Message content template for test failure log message: value. */
+  private static final String TEST_FAIL_VAL = " value";
+
+  /** Message content template for test failure log message: patch. */
+  private static final String TEST_FAIL_FROM = "Diff from ";
+
+  /** Message content template for test failure log message: failed on. */
+  private static final String TEST_FAIL_TO = " to ";
+
+  /** Message content template for test failure log message: value. */
+  private static final String TEST_FAIL_FAIL = " failed";
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @param message
+   *          Assert message.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check, final JsonValue out,
+      final String message) {
+    return out == null || !assertEquals(check, out, message);
+  }
+
+  /**
+   * Test helper: Verify merge of JSON patch on provided JSON value and verify
+   * result using provided expected JSON value.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          Source JSON value to be modified.
+   * @param patch
+   *          JSON patch to be done on source value.
+   * @param check
+   *          Expected modified JSON object (used for operation check).
+   */
+  protected void simpleMerge(final TestResult result, final JsonValue in,
+      final JsonValue patch, final JsonValue check) {
+    final JsonValue out = Json.createMergePatch(patch).apply(in);
+    if (operationFailed(check, out, MERGE_STR + " mismatch")) {
+      final String targetClassName = in.getValueType().name().toLowerCase();
+      result.fail(testName(MERGE_STR, targetClassName),
+          testMergeMessage(valueToString(in), valueToString(patch)));
+    }
+  }
+
+  /**
+   * Test helper: Verify diff on provided JSON values and verify result using
+   * provided expected JSON value.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param src
+   *          Source JSON value for diff.
+   * @param target
+   *          Target JSON value for diff.
+   * @param diff
+   *          Expected diff JSON object (used for operation check).
+   */
+  protected void simpleDiff(final TestResult result, final JsonValue src,
+      final JsonValue target, final JsonValue diff) {
+    final JsonValue out = Json.createMergeDiff(src, target).toJsonValue();
+    if (operationFailed(diff, out, DIFF_STR + " mismatch")) {
+      final String srcClassName = src.getValueType().name().toLowerCase();
+      final String targetClassName = target.getValueType().name().toLowerCase();
+      result.fail(testName(DIFF_STR, srcClassName, targetClassName),
+          testDiffMessage(valueToString(src), valueToString(target)));
+    }
+  }
+
+  /**
+   * Build test name for test failure log message.
+   * 
+   * @param operation
+   *          Name of operation.
+   * @param targetType
+   *          Name of the target (JSON value being modified) value type.
+   * @return Test name for test failure log message.
+   */
+  protected String testName(final String operation, final String targetType) {
+    final StringBuilder sb = new StringBuilder(
+        operation.length() + targetType.length() + 1);
+    sb.append(operation);
+    sb.append(' ');
+    sb.append(targetType);
+    return sb.toString();
+  }
+
+  /**
+   * Build test name for test failure log message.
+   * 
+   * @param operation
+   *          Name of operation.
+   * @param srcType
+   *          Name of the source (JSON value being used for modification) value
+   *          type.
+   * @param targetType
+   *          Name of the target (JSON value being modified) value type.
+   * @return Test name for test failure log message.
+   */
+  protected String testName(final String operation, final String srcType,
+      final String targetType) {
+    final StringBuilder sb = new StringBuilder(
+        operation.length() + srcType.length() + targetType.length() + 2);
+    sb.append(operation);
+    sb.append(' ');
+    sb.append(srcType);
+    sb.append(',');
+    sb.append(targetType);
+    return sb.toString();
+  }
+
+  /**
+   * Build message content for test failure log message.
+   * 
+   * @param in
+   *          Source JSON value to be modified.
+   * @param patch
+   *          JSON patch to be done on source value.
+   * @return Log message content.
+   */
+  protected String testMergeMessage(final String in, final String patch) {
+    final StringBuilder sb = new StringBuilder(
+        TEST_FAIL_PATCH.length() + TEST_FAIL_ON.length()
+            + TEST_FAIL_VAL.length() + patch.length() + in.length());
+    sb.append(TEST_FAIL_PATCH);
+    sb.append(patch);
+    sb.append(TEST_FAIL_ON);
+    sb.append(in);
+    sb.append(TEST_FAIL_VAL);
+    return sb.toString();
+  }
+
+  /**
+   * Build message content for test failure log message.
+   * 
+   * @param src
+   *          Source JSON value for diff.
+   * @param target
+   *          Target JSON value for diff.
+   * @return Log message content.
+   */
+  protected String testDiffMessage(final String src, final String target) {
+    final StringBuilder sb = new StringBuilder(
+        TEST_FAIL_FROM.length() + TEST_FAIL_TO.length()
+            + TEST_FAIL_FAIL.length() + src.length() + target.length());
+    sb.append(TEST_FAIL_FROM);
+    sb.append(src);
+    sb.append(TEST_FAIL_TO);
+    sb.append(target);
+    sb.append(TEST_FAIL_FAIL);
+    return sb.toString();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeNonObject.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeNonObject.java
new file mode 100644
index 0000000..dbcba22
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeNonObject.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * Checks scenario described in
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+ * Introduction</a>}: If the patch is anything other than an object, the result
+ * will always be to replace the entire target with the entire patch.
+ */
+public class MergeNonObject extends MergeCommon {
+
+  /**
+   * Creates an instance of RFC 7396 non object patch test.
+   */
+  MergeNonObject() {
+    super();
+  }
+
+  /**
+   * Test RFC 7396: Non object patch. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 7396: Non object patch");
+    System.out.println("Testing RFC 7396: Non object patch");
+    testStringOnEmptyObject(result);
+    testStringOnSimpleObject(result);
+    testStringOnSimpleArray(result);
+    testIntOnEmptyObject(result);
+    testIntOnSimpleObject(result);
+    testIntOnSimpleArray(result);
+    testBoolOnEmptyObject(result);
+    testBoolOnSimpleObject(result);
+    testBoolOnSimpleArray(result);
+    testArrayOnEmptyObject(result);
+    testArrayOnCompoundObject(result);
+    testArrayOnSimpleArray(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonValue patch = Json.createValue(STR_VALUE);
+    final JsonValue check = Json.createValue(STR_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectWithStr();
+    final JsonValue patch = Json.createValue(STR_VALUE);
+    final JsonValue check = Json.createValue(STR_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on empty JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array");
+    final JsonArray in = createStringArray2();
+    final JsonValue patch = Json.createValue(STR_VALUE);
+    final JsonValue check = Json.createValue(STR_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonValue patch = Json.createValue(INT_VALUE);
+    final JsonValue check = Json.createValue(INT_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectWithInt();
+    final JsonValue patch = Json.createValue(INT_VALUE);
+    final JsonValue check = Json.createValue(INT_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on empty JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array");
+    final JsonArray in = createIntArray2();
+    final JsonValue patch = Json.createValue(INT_VALUE);
+    final JsonValue check = Json.createValue(INT_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonValue patch = toJsonValue(BOOL_VALUE);
+    final JsonValue check = toJsonValue(BOOL_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectWithBool();
+    final JsonValue patch = toJsonValue(BOOL_VALUE);
+    final JsonValue check = toJsonValue(BOOL_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on empty JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array");
+    final JsonArray in = createBoolArray2();
+    final JsonValue patch = toJsonValue(BOOL_VALUE);
+    final JsonValue check = toJsonValue(BOOL_VALUE);
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonArray} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testArrayOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonArray on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonArray patch = createStringArray1();
+    final JsonArray check = createStringArray1();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonArray} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testArrayOnCompoundObject(final TestResult result) {
+    System.out.println(" - for JsonArray on compound JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonValue patch = createStringArray2();
+    final JsonValue check = createStringArray2();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonArray} on empty JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testArrayOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonArray on simple JSON array");
+    final JsonArray in = createBoolArray2();
+    final JsonValue patch = createIntArray2();
+    final JsonValue check = createIntArray2();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRFCSample.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRFCSample.java
new file mode 100644
index 0000000..37dd77f
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRFCSample.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonObject;
+
+import static jakarta.jsonp.tck.api.common.MergeRFCObject.*;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * Test based on
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC 7396: 3.
+ * Example</a>} objects.
+ */
+public class MergeRFCSample extends MergeCommon {
+
+  /**
+   * Creates an instance of RFC 7396 value replacing test.
+   */
+  MergeRFCSample() {
+    super();
+  }
+
+  /**
+   * Test RFC 7396: Adding non existing values. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 7396: Example JSON object");
+    System.out.println("Testing RFC 7396: Example JSON object");
+    testMerge(result);
+    testDiff(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 7396 patch for example objects.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMerge(final TestResult result) {
+    System.out.println(" - merge");
+    final JsonObject in = createRFCSourceObject();
+    final JsonObject patch = createRFCPatchObject();
+    final JsonObject check = createRFCTargetObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 diff for example objects.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testDiff(final TestResult result) {
+    System.out.println(" - diff");
+    final JsonObject in = createRFCSourceObject();
+    final JsonObject diff = createRFCPatchObject();
+    final JsonObject out = createRFCTargetObject();
+    simpleDiff(result, in, out, diff);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRemoveValue.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRemoveValue.java
new file mode 100644
index 0000000..039fb70
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeRemoveValue.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonObject;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * Checks scenario described in
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+ * Introduction</a>}: {@code null} values in the merge patch are given special
+ * meaning to indicate the removal of existing values in the target.
+ */
+public class MergeRemoveValue extends MergeCommon {
+
+  /**
+   * Creates an instance of RFC 7396 value removal test.
+   */
+  MergeRemoveValue() {
+    super();
+  }
+
+  /**
+   * Test RFC 7396: Removing existing values. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 7396: Remove existing values");
+    System.out.println("Testing RFC 7396: Remove existing values");
+    testStringOnEmptyObject(result);
+    testStringOnsimpleObject(result);
+    testIntOnEmptyObject(result);
+    testIntOnsimpleObject(result);
+    testBoolOnEmptyObject(result);
+    testBoolOnsimpleObject(result);
+    testObjectOnEmptyObject(result);
+    testObjectOnsimpleObject(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String to produce empty JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject patch = createPatchRemoveStr();
+    final JsonObject check = createEmptyObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnsimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectWithStr();
+    final JsonObject patch = createPatchRemoveStr();
+    final JsonObject check = createSimpleObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int to produce empty JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject patch = createPatchRemoveInt();
+    final JsonObject check = createEmptyObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnsimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectWithInt();
+    final JsonObject patch = createPatchRemoveInt();
+    final JsonObject check = createSimpleObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean to produce empty JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject patch = createPatchRemoveBool();
+    final JsonObject check = createEmptyObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnsimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectWithBool();
+    final JsonObject patch = createPatchRemoveBool();
+    final JsonObject check = createSimpleObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonObject} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject to produce empty JSON object");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject patch = createPatchRemoveObject();
+    final JsonObject check = createEmptyObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testObjectOnsimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on compoubnd JSON object");
+    final JsonObject in = createCompoundObjectWithObject();
+    final JsonObject patch = createPatchRemoveObject();
+    final JsonObject check = createCompoundObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeReplaceValue.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeReplaceValue.java
new file mode 100644
index 0000000..5d66b44
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeReplaceValue.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonObject;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * Checks scenario described in
+ * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+ * Introduction</a>}: If the target does contain the member, the value is
+ * replaced.
+ */
+public class MergeReplaceValue extends MergeCommon {
+
+  /**
+   * Creates an instance of RFC 7396 value replacing test.
+   */
+  MergeReplaceValue() {
+    super();
+  }
+
+  /**
+   * Test RFC 7396: Adding non existing values. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 7396: Replace existing values");
+    System.out.println("Testing RFC 7396: Replace existing values");
+    testStringOnsimpleObject(result);
+    testIntOnsimpleObject(result);
+    testBoolOnsimpleObject(result);
+    testObjectOnsimpleObject(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testStringOnsimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectMoveStr();
+    final JsonObject check = createSimpleObjectMoveStr();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testIntOnsimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectMoveInt();
+    final JsonObject check = createSimpleObjectMoveInt();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testBoolOnsimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectMoveBool();
+    final JsonObject check = createSimpleObjectMoveBool();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+  /**
+   * Test RFC 7396 patch and diff for {@code JsonObject} on compound JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testObjectOnsimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject patch = createSimpleObjectMoveObject();
+    final JsonObject check = createSimpleObjectMoveObject();
+    simpleMerge(result, in, patch, check);
+    simpleDiff(result, in, check, patch);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeTests.java
new file mode 100644
index 0000000..b9cd494
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/mergetests/MergeTests.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.mergetests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import org.junit.Test;
+
+// $Id$
+/**
+ * RFC 7396: JavaScript Object Notation (JSON) Merge Patch compatibility
+ * tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc7396">RFC 7396</a>}.
+ */
+public class MergeTests {
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+   * Introduction</a>}: If the provided merge patch contains members that do not
+   * appear within the target, those members are added.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonMergeAddValueTest
+   * @assertion_ids: JSONP:JAVADOC:575; JSONP:JAVADOC:576; JSONP:JAVADOC:616;
+   *                 JSONP:JAVADOC:617; JSONP:JAVADOC:620; JSONP:JAVADOC:654;
+   *                 JSONP:JAVADOC:655;
+   * @test_Strategy: Test API response on various JSON values.
+   */
+  @Test
+  public void jsonMergeAddValueTest() throws Fault {
+    MergeAddValue addTest = new MergeAddValue();
+    final TestResult result = addTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+   * Introduction</a>}: If the target does contain the member, the value is
+   * replaced.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonMergeReplaceValueTest
+   * @assertion_ids: JSONP:JAVADOC:575; JSONP:JAVADOC:576; JSONP:JAVADOC:616;
+   *                 JSONP:JAVADOC:617; JSONP:JAVADOC:654; JSONP:JAVADOC:655;
+   * @test_Strategy: Test API response on various JSON values.
+   */
+  @Test
+  public void jsonMergeReplaceValueTest() throws Fault {
+    MergeReplaceValue replaceTest = new MergeReplaceValue();
+    final TestResult result = replaceTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+   * Introduction</a>}: {@code null} values in the merge patch are given special
+   * meaning to indicate the removal of existing values in the target.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonMergeRemoveValueTest
+   * @assertion_ids: JSONP:JAVADOC:575; JSONP:JAVADOC:576; JSONP:JAVADOC:616;
+   *                 JSONP:JAVADOC:617; JSONP:JAVADOC:654; JSONP:JAVADOC:655;
+   * @test_Strategy: Test API response on various JSON values.
+   */
+  @Test
+  public void jsonMergeRemoveValueTest() throws Fault {
+    MergeRemoveValue removeTest = new MergeRemoveValue();
+    final TestResult result = removeTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc7396#section-1">RFC 7396: 1.
+   * Introduction</a>}: If the patch is anything other than an object, the
+   * result will always be to replace the entire target with the entire patch.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonMergeNonObjectTest
+   * @assertion_ids: JSONP:JAVADOC:575; JSONP:JAVADOC:576; JSONP:JAVADOC:616;
+   *                 JSONP:JAVADOC:617; JSONP:JAVADOC:654; JSONP:JAVADOC:655;
+   *                 JSONP:JAVADOC:583; JSONP:JAVADOC:584;
+   * @test_Strategy: Test API response on various JSON values.
+   */
+  @Test
+  public void jsonMergeNonObjectTest() throws Fault {
+    MergeNonObject nonObjTest = new MergeNonObject();
+    final TestResult result = nonObjTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on Test based on
+   * {@see <a href="https://tools.ietf.org/html/rfc7396#section-3">RFC 7396: 3.
+   * Example</a>} objects.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonMergeRFCSampleTest
+   * @assertion_ids: JSONP:JAVADOC:575; JSONP:JAVADOC:576; JSONP:JAVADOC:616;
+   *                 JSONP:JAVADOC:617; JSONP:JAVADOC:654; JSONP:JAVADOC:655;
+   * @test_Strategy: Test API response on RFC example objects.
+   */
+  @Test
+  public void jsonMergeRFCSampleTest() throws Fault {
+    MergeRFCSample rfcSampleTest = new MergeRFCSample();
+    final TestResult result = rfcSampleTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/CommonOperation.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/CommonOperation.java
new file mode 100644
index 0000000..0ac6675
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/CommonOperation.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests.
+ */
+public abstract class CommonOperation {
+
+  /** Message content template for test failure log message: operation name. */
+  private static final String TEST_FAIL_OP = " operation";
+
+  /** Message content template for test failure log message: path. */
+  private static final String TEST_FAIL_FOR = " for ";
+
+  /** Message content template for test failure log message: failed. */
+  private static final String TEST_FAIL_FAI = " failed";
+
+  /**
+   * Message content template for test failure log message: on target type
+   * prefix.
+   */
+  private static final String TEST_FAIL_ON1 = " on JSON ";
+
+  /**
+   * Message content template for test failure log message: on target type
+   * suffix.
+   */
+  private static final String TEST_FAIL_ON2 = " value";
+
+  /**
+   * Message content template for test failure log message: patching execution
+   * method.
+   */
+  private static final String TEST_FAIL_MET = " using ";
+
+  /**
+   * Creates an instance of JavaScript Object Notation (JSON) compatibility
+   * tests.
+   */
+  protected CommonOperation() {
+    super();
+  }
+
+  /**
+   * Tested operation name, e.g. {@code "ADD"}, {@code "REPLACE"},
+   * {@code "MOVE"}. Child class callback.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  protected abstract String operationName();
+
+  /**
+   * Create and initialize patch builder to contain patch operation to be
+   * applied. Child class callback.
+   * 
+   * @param path
+   *          JSON path of operation.
+   * @param value
+   *          JSON value used in patch operation.
+   * @return Patch builder containing operation to be applied.
+   */
+  protected abstract JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value);
+
+  /**
+   * Update patch builder to contain next patch operation to be applied. Child
+   * class callback.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          JSON path of operation.
+   * @param value
+   *          JSON value used in patch operation.
+   * @return Patch builder containing operation to be applied.
+   */
+  protected abstract JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value);
+
+  /**
+   * Test helper: Verify simple operation on provided JSON value and verify
+   * result using provided expected JSON value. Operation execution is done
+   * using all known methods to build and apply JSON patch.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON value to be modified.
+   * @param check
+   *          Expected modified JSON object (used for operation check).
+   * @param path
+   *          JSON path of operation.
+   * @param value
+   *          JSON value used in patch operation.
+   */
+  protected void simpleOperation(final TestResult result, final JsonValue in,
+      final JsonValue check, final String path, final Object value) {
+    final JsonPatchBuilder builder = createOperationBuilder(path, value);
+    final JsonPatch patch = builder.build();
+    JsonValue out;
+    try {
+      out = patchApply(patch, in);
+    } catch (JsonException e) {
+      out = null;
+      System.out.println(
+          "   Exception for path \"" + path + "\" on " + valueToString(in));
+      System.out.println("     " + e.getMessage());
+    }
+    if (operationFailed(check, out)) {
+      final String targetClassName = in.getValueType().name().toLowerCase();
+      final String operation = valueToString(patch.toJsonArray());
+      System.out.println("     " + operation);
+      result.fail(testName(path, targetClassName),
+          testMessage(operation, path, valueToString(in)));
+    }
+  }
+
+  /**
+   * Test helper: Verify set of operations on provided JSON value and verify
+   * result using provided expected JSON value. Verification is done using all
+   * known methods to build and apply JSON patch. This method allows custom
+   * patching of JSON array. Used for operations without value operand, e.g.
+   * REMOVE. Operation builder callback will receive {@code null} as value.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON array to be modified.
+   * @param check
+   *          Expected modified JSON array (used for operation check).
+   * @param paths
+   *          JSON paths array of operations.
+   */
+  protected void complexOperation(final TestResult result, final JsonArray in,
+      final JsonArray check, final String[] paths) {
+    final Object[] values = new Object[paths.length];
+    for (int i = 0; i < paths.length; i++) {
+      values[i] = null;
+    }
+    complexOperation(result, in, check, paths, values);
+  }
+
+  /**
+   * Test helper: Verify set of operations on provided JSON value and verify
+   * result using provided expected JSON value. Verification is done using all
+   * known methods to build and apply JSON patch. This method allows custom
+   * patching of JSON array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON array to be modified.
+   * @param check
+   *          Expected modified JSON array (used for operation check).
+   * @param paths
+   *          JSON paths array of operations. Pairs of {@code paths[i]} and
+   *          {@code values[i]} are used for individual operations.
+   * @param values
+   *          JSON values array used in patch operations.
+   */
+  protected void complexOperation(final TestResult result, final JsonArray in,
+      final JsonArray check, final String[] paths, final Object[] values) {
+    if (paths.length != values.length) {
+      throw new IllegalArgumentException(
+          "Number of paths does not match number of indexes");
+    }
+    final JsonPatchBuilder builder = prepareComplexBuilder(paths, values);
+    final JsonPatch patch = builder.build();
+    final JsonValue out = patchApply(patch, in);
+    if (operationFailed(check, out)) {
+      final String operations = valueToString(patch.toJsonArray());
+      final String targetClassName = in.getValueType().name().toLowerCase();
+      System.out.println("     " + operations);
+      result.fail(testName(paths, targetClassName),
+          testMessage(operations, paths, valueToString(in)));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+  /**
+   * Builds JSON patch builder with set of operations stored in {@code paths}
+   * and {@code values}.
+   * 
+   * @param paths
+   *          JSON paths array of operations. Pairs of {@code paths[i]} and
+   *          {@code values[i]} are used for individual operations.
+   * @param values
+   *          JSON values array used in patch operations.
+   */
+  private JsonPatchBuilder prepareComplexBuilder(final String[] paths,
+      final Object[] values) {
+    JsonPatchBuilder builder = Json.createPatchBuilder();
+    for (int i = 0; i < paths.length; i++) {
+      builder = updateOperationBuilder(builder, paths[i], values[i]);
+    }
+    return builder;
+  }
+
+  /**
+   * Test helper: Verify that operation on provided JSON value fails. Operation
+   * execution is done using all known methods to build and apply JSON patch.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON value to be modified.
+   * @param path
+   *          JSON path of operation.
+   * @param value
+   *          JSON value used in patch operation.
+   */
+  protected void simpleOperationFail(final TestResult result,
+      final JsonValue in, final String path, final Object value) {
+    try {
+      final JsonPatch patch = createOperationBuilder(path, value).build();
+      patchApply(patch, in);
+      final String targetClassName = in.getValueType().name().toLowerCase();
+      final String operation = valueToString(patch.toJsonArray());
+      System.out.println(
+          "   Failed for path \"" + path + "\" on " + valueToString(in));
+      System.out.println("     " + operation);
+      result.fail(testName(path, targetClassName),
+          testMessage(operation, path, valueToString(in)));
+    } catch (JsonException e) {
+      // There are too many combinations to log them.
+      // System.out.println(" - Expected exception: "+e.getMessage());
+    }
+  }
+
+  /**
+   * Get source class name.
+   * 
+   * @param value
+   *          JSON value to search for class name.
+   * @return Class name of provided JSON value or {@code null} when this value
+   *         has been {@code null}.
+   */
+  protected String getSrcName(final Object value) {
+    return value != null ? value.getClass().getSimpleName() : null;
+  }
+
+  /**
+   * Get source classes names.
+   * 
+   * @param values
+   *          JSON values to search for class name.
+   * @return Class name of provided JSON value or {@code null} when this value
+   *         has been {@code null}.
+   */
+  protected String[] getSrcNames(final Object[] values) {
+    if (values == null) {
+      return null;
+    }
+    final String[] names = new String[values.length];
+    for (int i = 0; i < values.length; i++) {
+      names[i] = values[i] != null ? values[i].getClass().getSimpleName()
+          : null;
+    }
+    return names;
+  }
+
+  /**
+   * Build test name for test failure log message.
+   * 
+   * @param path
+   *          JSON patch operation source path.
+   * @param targetType
+   *          Name of target (JSON value being modified) value type.
+   * @return Test name for test failure log message.
+   */
+  protected String testName(final String path, final String targetType) {
+    final String operationName = operationName();
+    final int pathLen = path != null ? path.length() + 1 : 0;
+    final StringBuilder sb = new StringBuilder(
+        operationName.length() + pathLen + targetType.length() + 1);
+    sb.append(operationName);
+    if (pathLen > 0) {
+      sb.append(' ');
+      sb.append(path);
+    }
+    sb.append(' ');
+    sb.append(targetType);
+    return sb.toString();
+  }
+
+  /**
+   * Build test name for test failure log message.
+   * 
+   * @param paths
+   *          JSON patch operation source paths.
+   * @param targetType
+   *          Name of target (JSON value being modified) value type.
+   * @return Test name for test failure log message.
+   */
+  protected String testName(final String[] paths, final String targetType) {
+    final String operationName = operationName();
+    final int pathsLen = paths != null ? paths.length : 0;
+    int pathsSize = 0;
+    for (int i = 0; i < pathsLen; i++) {
+      pathsSize += paths[i] != null ? paths[i].length() : NULL.length();
+      if (i > 0) {
+        pathsSize += 1;
+      }
+    }
+    if (pathsLen > 1) {
+      pathsSize += 2;
+    }
+    final StringBuilder sb = new StringBuilder(
+        operationName.length() + pathsSize + targetType.length() + 2);
+    sb.append(operationName);
+    sb.append(' ');
+    if (pathsLen > 1) {
+      sb.append('[');
+    }
+    for (int i = 0; i < pathsLen; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      sb.append(paths[i] != null ? paths[i] : NULL);
+    }
+    if (pathsLen > 1) {
+      sb.append(']');
+    }
+    sb.append(' ');
+    sb.append(targetType);
+    return sb.toString();
+  }
+
+  /**
+   * Build message content for test failure log message.
+   * 
+   * @param operation
+   *          JSON patch operation being executed.
+   * @param path
+   *          JSON patch operation source path.
+   * @param value
+   *          Target value being modified.
+   * @return Log message content.
+   */
+  protected String testMessage(final String operation, final String path,
+      final String value) {
+    final int tarLen = value != null
+        ? TEST_FAIL_ON1.length() + TEST_FAIL_ON2.length() + value.length()
+        : 0;
+    final StringBuilder sb = new StringBuilder(
+        operation.length() + TEST_FAIL_OP.length() + TEST_FAIL_FOR.length()
+            + path.length() + TEST_FAIL_FAI.length() + tarLen);
+    sb.append(operation);
+    sb.append(TEST_FAIL_OP);
+    sb.append(TEST_FAIL_FOR);
+    sb.append(path);
+    sb.append(TEST_FAIL_FAI);
+    if (tarLen > 0) {
+      sb.append(TEST_FAIL_ON1);
+      sb.append(value);
+      sb.append(TEST_FAIL_ON2);
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Build message content for test failure log message.
+   * 
+   * @param operation
+   *          JSON patch operation being executed.
+   * @param paths
+   *          JSON patch operation source paths.
+   * @param value
+   *          Target value being modified.
+   * @return Log message content.
+   */
+  protected String testMessage(final String operation, final String[] paths,
+      final String value) {
+    final int tarLen = value != null
+        ? TEST_FAIL_ON1.length() + TEST_FAIL_ON2.length() + value.length()
+        : 0;
+    final int pathsLen = paths != null ? paths.length : 0;
+    int pathsSize = 0;
+    for (int i = 0; i < pathsLen; i++) {
+      pathsSize += paths[i] != null ? paths[i].length() : NULL.length();
+      if (i > 0) {
+        pathsSize += 1;
+      }
+    }
+    if (pathsLen > 1) {
+      pathsSize += 2;
+    }
+    final StringBuilder sb = new StringBuilder(
+        operation.length() + TEST_FAIL_OP.length() + TEST_FAIL_FOR.length()
+            + pathsSize + TEST_FAIL_FAI.length() + tarLen);
+    sb.append(operation);
+    sb.append(TEST_FAIL_OP);
+    sb.append(TEST_FAIL_FOR);
+    if (pathsLen > 1) {
+      sb.append('[');
+    }
+    for (int i = 0; i < pathsLen; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      sb.append(paths[i] != null ? paths[i] : NULL);
+    }
+    if (pathsLen > 1) {
+      sb.append(']');
+    }
+    sb.append(TEST_FAIL_FAI);
+    if (tarLen > 0) {
+      sb.append(TEST_FAIL_ON1);
+      sb.append(value);
+      sb.append(TEST_FAIL_ON2);
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Build message content for test failure log message.
+   * 
+   * @param paths
+   *          JSON patch operation source path.
+   * @param targetType
+   *          Name of target (JSON value being modified) value type.
+   * @return Log message content.
+   */
+  protected String testMessage(final String[] paths, final String targetType) {
+    final String operationName = operationName();
+    final int tarLen = targetType != null
+        ? TEST_FAIL_ON1.length() + TEST_FAIL_ON2.length() + targetType.length()
+        : 0;
+    int pathsLen = 0;
+    for (int i = 0; i < paths.length; i++) {
+      pathsLen += paths[i] != null ? paths[i].length() : NULL.length();
+      if (i > 0) {
+        pathsLen += 1;
+      }
+    }
+    if (paths.length > 1) {
+      pathsLen += 2;
+    }
+    final StringBuilder sb = new StringBuilder(
+        operationName.length() + TEST_FAIL_OP.length() + TEST_FAIL_FOR.length()
+            + pathsLen + TEST_FAIL_FAI.length() + tarLen);
+    sb.append(operationName);
+    sb.append(TEST_FAIL_OP);
+    sb.append(TEST_FAIL_FOR);
+    if (paths.length > 1) {
+      sb.append('[');
+    }
+    for (int i = 0; i < paths.length; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      sb.append(paths[i] != null ? paths[i] : NULL);
+    }
+    if (paths.length > 1) {
+      sb.append(']');
+    }
+    sb.append(TEST_FAIL_FAI);
+    if (tarLen > 0) {
+      sb.append(TEST_FAIL_ON1);
+      sb.append(targetType);
+      sb.append(TEST_FAIL_ON2);
+    }
+    return sb.toString();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchCreate.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchCreate.java
new file mode 100644
index 0000000..e5fc243
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchCreate.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * JavaScript Object Notation (JSON) compatibility tests: {@link JsonPatch} API
+ * factory methods added in JSON-P 1.1.<br>
+ */
+public class PatchCreate {
+
+  /**
+   * Creates an instance of {@link JsonPatch} API factory methods added in
+   * JSON-P 1.1 test.
+   */
+  PatchCreate() {
+    super();
+  }
+
+  /**
+   * Test {@link JsonPatch} factory method added in JSON-P 1.1.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonPatch API factory methods added in JSON-P 1.1.");
+    System.out.println("JsonPatch API factory methods added in JSON-P 1.1.");
+    testCreateDiff(result);
+    testCreatePatch(result);
+    testCreatePatchBuilder(result);
+    return result;
+  }
+
+  /**
+   * Test {@link Json#createDiff(JsonStructure,JsonStructure)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreateDiff(final TestResult result) {
+    System.out.println(" - Json#createDiff(JsonStructure,JsonStructure)");
+    final JsonObject src = createSimpleObject();
+    final JsonObject trg = createSimpleObjectWithStr();
+    final JsonPatch patch = Json.createDiff(src, trg);
+    final JsonObject out = patch.apply(src);
+    if (operationFailed(trg, out)) {
+      result.fail("createDiff(JsonStructure,JsonStructure)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(trg));
+    }
+  }
+
+  /**
+   * Test {@link Json#createPatch(JsonArray)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreatePatch(final TestResult result) {
+    System.out.println(" - Json#createPatch(JsonArray)");
+    final JsonObject src = createSimpleObject();
+    final JsonObject trg = createSimpleObjectWithStr();
+    final JsonArray patchArray = Json.createDiff(src, trg).toJsonArray();
+    final JsonPatch patch = Json.createPatch(patchArray);
+    final JsonObject out = patch.apply(src);
+    if (operationFailed(trg, out)) {
+      result.fail("createPatch(JsonArray)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(trg));
+    }
+  }
+
+  /**
+   * Test {@link Json#createPatchBuilder(JsonArray)} method.
+   * 
+   * @param result
+   *          Test suite result.
+   */
+  private void testCreatePatchBuilder(final TestResult result) {
+    System.out.println(" - Json#createPatchBuilder(JsonArray)");
+    final JsonObject src = createSimpleObject();
+    final JsonObject trg = createSimpleObjectWithStr();
+    final JsonArray patchArray = Json.createDiff(src, trg).toJsonArray();
+    final JsonPatchBuilder patchBuilder = Json.createPatchBuilder(patchArray);
+    final JsonObject out = patchBuilder.build().apply(src);
+    if (operationFailed(trg, out)) {
+      result.fail("createPatchBuilder(JsonArray)", "Builder output "
+          + valueToString(out) + " value shall be " + valueToString(trg));
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null || !assertEquals(check, out);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationAdd.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationAdd.java
new file mode 100644
index 0000000..7318284
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationAdd.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonStructure;
+import jakarta.json.Json;
+import jakarta.json.JsonPointer;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements
+ * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+ * 4.1. add</a>} tests.
+ */
+class PatchOperationAdd extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "ADD";
+
+  /**
+   * Creates an instance of RFC 6902 add operation test.
+   */
+  PatchOperationAdd() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 add operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 add operation");
+    System.out.println("Testing RFC 6902 add operation:");
+    testAddStringOnEmptyObject(result);
+    testAddStringOnSimpleObject(result);
+    testAddStringOnEmptyArray(result);
+    testAddStringOnSimpleArray(result);
+    testAddStringOnSimpleArray2(result);
+    testAddIntOnEmptyObject(result);
+    testAddIntOnSimpleObject(result);
+    testAddIntOnEmptyArray(result);
+    testAddIntOnSimpleArray(result);
+    testAddIntOnSimpleArray2(result);
+    testAddBooleanOnEmptyObject(result);
+    testAddBooleanOnSimpleObject(result);
+    testAddBooleanOnEmptyArray(result);
+    testAddBooleanOnSimpleArray(result);
+    testAddBooleanOnSimpleArray2(result);
+    testAddObjectOnEmptyObject(result);
+    testAddObjectOnSimpleObject(result);
+    testAddObjectOnEmptyArray(result);
+    testAddObjectOnSimpleArray(result);
+    testAddObjectOnSimpleArray2(result);
+    testAddArrayToReplaceObject(result);
+    testAddArrayToReplaceDocument(result);
+    testAddStringArrayToStringArray(result);
+    testAddStringToNonExistingObject(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectStr();
+    simpleOperation(result, in, check, STR_PATH, STR_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code String} on empty JSON array. Only
+   * allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnEmptyArray(final TestResult result) {
+    System.out.println(" - for String on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithStr();
+    simpleOperation(result, in, check, "/0", STR_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithStr();
+    simpleOperation(result, in, check, STR_PATH, STR_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code String} on simple JSON array. Using
+   * index {@code 0} to add {@code String} before already existing element and
+   * index {@code 1} to add {@code String} after already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 1");
+    final JsonArray in = createStringArray1();
+    final JsonArray checkBefore = createSimpleStringArrayWithStrBefore();
+    final JsonArray checkAfter = createSimpleStringArrayWithStrAfter();
+    // Add before.
+    simpleOperation(result, in, checkBefore, "/0", STR_VALUE);
+    // Add after.
+    simpleOperation(result, in, checkAfter, "/1", STR_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code String}s on simple JSON array.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code String} at the end, in the middle and at the beginning of
+   * this array.
+   * <li>Adding {@code String} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createSimpleStringArray5();
+    complexOperation(result, in, check, new String[] { "/2", "/1", "/0" },
+        new String[] { STR_VALUE_5, STR_VALUE_3, STR_VALUE_1 });
+    complexOperation(result, in, check, new String[] { "/0", "/2", "/4" },
+        new String[] { STR_VALUE_1, STR_VALUE_3, STR_VALUE_5 });
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectInt();
+    simpleOperation(result, in, check, INT_PATH, INT_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code int} on empty JSON array. Only
+   * allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnEmptyArray(final TestResult result) {
+    System.out.println(" - for int on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithInt();
+    simpleOperation(result, in, check, "/0", INT_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithInt();
+    simpleOperation(result, in, check, INT_PATH, INT_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code int} on simple JSON array. Using
+   * index {@code 0} to add {@code int} before already existing element and
+   * index {@code 1} to add {@code int} after already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 1");
+    final JsonArray in = createIntArray1();
+    final JsonArray checkBefore = createSimpleIntArrayWithIntBefore();
+    final JsonArray checkAfter = createSimpleIntArrayWithIntAfter();
+    // Add before.
+    simpleOperation(result, in, checkBefore, "/0", INT_VALUE);
+    // Add after.
+    simpleOperation(result, in, checkAfter, "/1", INT_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code int}s on simple JSON array. Starting
+   * with an array of size 2.
+   * <ul>
+   * <li>Adding {@code int} at the end, in the middle and at the beginning of
+   * this array.
+   * <li>Adding {@code int} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray in = createIntArray2();
+    final JsonArray check = createSimpleIntArray5();
+    complexOperation(result, in, check, new String[] { "/2", "/1", "/0" },
+        new Integer[] { INT_VALUE_5, INT_VALUE_3, INT_VALUE_1 });
+    complexOperation(result, in, check, new String[] { "/0", "/2", "/4" },
+        new Integer[] { INT_VALUE_1, INT_VALUE_3, INT_VALUE_5 });
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBooleanOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectBool();
+    simpleOperation(result, in, check, BOOL_PATH, BOOL_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code boolean} on empty JSON array. Only
+   * allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBooleanOnEmptyArray(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithBool();
+    simpleOperation(result, in, check, "/0", BOOL_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBooleanOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithBool();
+    simpleOperation(result, in, check, BOOL_PATH, BOOL_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code boolean} on simple JSON array. Using
+   * index {@code 0} to add {@code boolean} before already existing element and
+   * index {@code 1} to add {@code boolean} after already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBooleanOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 1");
+    final JsonArray in = createBoolArray1();
+    final JsonArray checkBefore = createSimpleBoolArrayWithBoolBefore();
+    final JsonArray checkAfter = createSimpleBoolArrayWithBoolAfter();
+    // Add before.
+    simpleOperation(result, in, checkBefore, "/0", BOOL_FALSE);
+    // Add after.
+    simpleOperation(result, in, checkAfter, "/1", BOOL_FALSE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code boolean}s on simple JSON array.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code boolean} at the end, in the middle and at the beginning
+   * of this array.
+   * <li>Adding {@code boolean} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBooleanOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray in = createBoolArray2();
+    final JsonArray check = createSimpleBoolArray5();
+    complexOperation(result, in, check, new String[] { "/2", "/1", "/0" },
+        new Boolean[] { BOOL_TRUE, BOOL_TRUE, BOOL_FALSE });
+    complexOperation(result, in, check, new String[] { "/0", "/2", "/4" },
+        new Boolean[] { BOOL_FALSE, BOOL_TRUE, BOOL_TRUE });
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code JsonObject} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectObject();
+    simpleOperation(result, in, check, OBJ_PATH, OBJ_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code JsonObject} on empty JSON array.
+   * Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnEmptyArray(final TestResult result) {
+    System.out.println(" - for JsonObject on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithObject();
+    simpleOperation(result, in, check, "/0", OBJ_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectWithObject();
+    simpleOperation(result, in, check, OBJ_PATH, OBJ_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code JsonObject} on simple JSON array.
+   * Using index {@code 0} to add {@code JsonObject} before already existing
+   * element and index {@code 1} to add {@code JsonObject} after already
+   * existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 1");
+    final JsonArray in = createObjectArray1();
+    final JsonArray checkBefore = createSimpleObjectArrayWithObjectBefore();
+    final JsonArray checkAfter = createSimpleObjectArrayWithObjectAfter();
+    // Add before.
+    simpleOperation(result, in, checkBefore, "/0", OBJ_VALUE);
+    // Add after.
+    simpleOperation(result, in, checkAfter, "/1", OBJ_VALUE);
+  }
+
+  /**
+   * Test RFC 6902 add operation for {@code JsonObject}s on simple JSON array.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code JsonObject} at the end, in the middle and at the
+   * beginning of this array.
+   * <li>Adding {@code JsonObject} at the beginning, in the middle and at the
+   * end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray in = createObjectArray2();
+    final JsonArray check = createSimpleObjectArray5();
+    complexOperation(result, in, check, new String[] { "/2", "/1", "/0" },
+        new JsonObject[] { OBJ_VALUE_5, OBJ_VALUE_3, OBJ_VALUE_1 });
+    complexOperation(result, in, check, new String[] { "/0", "/2", "/4" },
+        new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_3, OBJ_VALUE_5 });
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test that existing target object is replaced by specified array when ADD
+   * operation is applied.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>}:<br>
+   * When the operation is applied, the target location MUST reference one of:
+   * <ul>
+   * <li>A member to add to an existing object - whereupon the supplied value is
+   * added to that object at the indicated location. If the member already
+   * exists, it is replaced by the specified value.</li>
+   * <li>...</li>
+   * </ul>
+   */
+  private void testAddArrayToReplaceObject(final TestResult result) {
+    System.out.println(" - for JsonArray to replace JsonObject");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectWithObjectReplaced();
+    final JsonArray value = createSimpleStringArray5();
+    simpleOperation(result, in, check, DEF_OBJ_PATH, value);
+  }
+
+  /**
+   * Test that whole document is replaced by specified array when ADD operation
+   * is applied with root pointer.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>}:<br>
+   * When the operation is applied, the target location MUST reference one of:
+   * <ul>
+   * <li>The root of the target document - whereupon the specified value becomes
+   * the entire content of the target document.</li>
+   * <li>...</li>
+   * </ul>
+   */
+  private void testAddArrayToReplaceDocument(final TestResult result) {
+    System.out.println(" - for JsonArray to replace whole document");
+    final JsonObject in = createCompoundObject();
+    final JsonArray check = createSimpleStringArray5();
+    final JsonArray value = createSimpleStringArray5();
+    // Instance being replaced is JsonObject, instance being added is JsonArray.
+    // The only API method allowing
+    // this is the one working with JsonStructure. New builder instance is used
+    // for each of the cases.
+    final JsonPatch patch1 = builderAdd(Json.createPatchBuilder(), "", value)
+        .build();
+    final JsonValue out1 = patch1.apply((JsonStructure) in);
+    if (!assertEquals(check, out1)) {
+      final String className = value.getClass().getSimpleName();
+      result.fail("ADD " + className + " to compound object",
+          "ADD operation for " + className + " failed on compound value");
+    }
+  }
+
+  /**
+   * Test ADD operation of an array of {@code String}s into existing array of
+   * {@code String}s. This scenario is inspired by
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>} operation example {@code { "op": "add", "path": "/a/b/c",
+   * "value": [ "foo", "bar" ] }} and following explanation of this operation on
+   * an array:
+   * <ul>
+   * <li>An element to add to an existing array - whereupon the supplied value
+   * is added to the array at the indicated location. Any elements at or above
+   * the specified index are shifted one position to the right. The specified
+   * index MUST NOT be greater than the number of elements in the array. If the
+   * "-" character is used to index the end of the array (see [RFC6901]), this
+   * has the effect of appending the value to the array.</li>
+   * </ul>
+   */
+  private void testAddStringArrayToStringArray(final TestResult result) {
+    System.out.println(" - for String array to be added to existing String array");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createStringArray2WithStringArrayInTheMiddle();
+    final JsonArray value = createStringInnerArray2();
+    simpleOperation(result, in, check, "/1", value);
+  }
+
+  /**
+   * Test ADD operation on non existing JsonObject. This scenario is described
+   * in {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC
+   * 6902: 4.1. add</a>} error handling samples. Test is trying to ADD value {
+   * "address" : "In a galaxy far far away"} into object { "name" : "John Smith"
+   * } using path "/child/address". Even "/child" path does not exist so this
+   * operation must fail.
+   * 
+   */
+  private void testAddStringToNonExistingObject(final TestResult result) {
+    System.out.println(" - for String to be added to non existing JsonObject");
+    final JsonObject in = createSimpleObject();
+    final JsonValue value = Json.createValue(STR_VALUE);
+    final String path = DEF_OBJ_PATH + STR_PATH;
+    final JsonPointer ptr = Json.createPointer(path);
+    simpleOperationFail(result, in, path, value);
+  }
+
+  /**
+   * Tested operation name {@code "MOVE"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain ADD operation to be applied.
+   * 
+   * @param path
+   *          JSON path of value to be added.
+   * @param value
+   *          JSON Value to be added.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    return builderAdd(Json.createPatchBuilder(), path, value);
+  }
+
+  /**
+   * Update patch builder to contain next ADD operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          JSON path of value to be added.
+   * @param value
+   *          JSON Value to be added.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    return builderAdd(builder, path, value);
+  }
+
+  /**
+   * Add {@code value} at {@code path} to provided JSON patch builder.
+   * 
+   * @param builder
+   *          Target JSON patch builder.
+   * @param path
+   *          JSON path of value to be added.
+   * @param value
+   *          Value to be added at given JSON path.
+   * @return JSON patch builder containing new {@code value} at {@code path}
+   *         added.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  private static JsonPatchBuilder builderAdd(final JsonPatchBuilder builder,
+      final String path, final Object value) {
+    switch (JsonValueType.getType(value.getClass())) {
+    case String:
+      return builder.add(path, (String) value);
+    case Integer:
+      return builder.add(path, ((Integer) value).intValue());
+    case Boolean:
+      return builder.add(path, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.add(path, (JsonValue) value);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationCopy.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationCopy.java
new file mode 100644
index 0000000..f0765c6
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationCopy.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements
+ * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.5">RFC 6902:
+ * 4.5. copy</a>} tests.
+ */
+public class PatchOperationCopy extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "COPY";
+
+  /**
+   * Creates an instance of RFC 6902 replace operation test.
+   */
+  PatchOperationCopy() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 COPY operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 copy operation");
+    System.out.println("Testing RFC 6902 copy operation:");
+    testCopyStringOnSimpleObject(result);
+    testCopyStringOnSimpleArray(result);
+    testCopyIntOnSimpleObject(result);
+    testCopyIntOnSimpleArray(result);
+    testCopyBoolOnSimpleObject(result);
+    testCopyBoolOnSimpleArray(result);
+    testCopyObjectOnSimpleObject(result);
+    testCopyObjectOnSimpleArray(result);
+    testCopyStringOnCompoundObject(result);
+    testCopyOfNonExistingLocationInObject(result);
+    testCopyOfNonExistingLocationInArray(result);
+
+    return result;
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectCopyStr();
+    simpleOperation(result, in, check, STR_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code String} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray in = createStringArray2();
+    simpleOperation(result, in, createStringArray2Copy1to0(), "/1", "/0");
+    simpleOperation(result, in, createStringArray2Copy0to2(), "/0", "/2");
+    simpleOperation(result, in, createStringArray2Copy0to1(), "/0", "/1");
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectCopyInt();
+    simpleOperation(result, in, check, INT_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code int} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray in = createIntArray2();
+    simpleOperation(result, in, createIntArray2Copy1to0(), "/1", "/0");
+    simpleOperation(result, in, createIntArray2Copy0to2(), "/0", "/2");
+    simpleOperation(result, in, createIntArray2Copy0to1(), "/0", "/1");
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectCopyBool();
+    simpleOperation(result, in, check, BOOL_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code boolean} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray in = createBoolArray2();
+    simpleOperation(result, in, createBoolArray2Copy1to0(), "/1", "/0");
+    simpleOperation(result, in, createBoolArray2Copy0to2(), "/0", "/2");
+    simpleOperation(result, in, createBoolArray2Copy0to1(), "/0", "/1");
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject check = createSimpleObjectCopyObject();
+    simpleOperation(result, in, check, OBJ_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code JsonObject} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray in = createObjectArray2();
+    simpleOperation(result, in, createObjectArray2Copy1to0(), "/1", "/0");
+    simpleOperation(result, in, createObjectArray2Copy0to2(), "/0", "/2");
+    simpleOperation(result, in, createObjectArray2Copy0to1(), "/0", "/1");
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for {@code String} on compound JSON object.
+   * Copied value overwrites an existing value.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testCopyStringOnCompoundObject(final TestResult result) {
+    System.out.println(" - for String on compound JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectCopyValue();
+    simpleOperation(result, in, check, DEF_PATH, DEF_OBJ_PATH + DEF_PATH);
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test RFC 6902 COPY operation for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.5. copy</a>} defines:<br>
+   * The "from" location MUST exist for the operation to be successful.
+   */
+  private void testCopyOfNonExistingLocationInObject(final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final Object[] values = new Object[] { OBJ_PATH, BOOL_PATH, INT_PATH,
+        STR_PATH };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, objsIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6902 COPY operation for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.5. copy</a>} defines:<br>
+   * The "from" location MUST exist for the operation to be successful.
+   */
+  private void testCopyOfNonExistingLocationInArray(final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2() };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    final Object[] values = new Object[] { "/0", "/1", "/2", "/5", "/1" };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, arraysIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Tested operation name {@code "COPY"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain COPY operation to be
+   * applied.
+   * 
+   * @param path
+   *          Source JSON path of COPY operation.
+   * @param value
+   *          Target JSON path of COPY operation. Must be instance of
+   *          {@link String}.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    if (value instanceof String) {
+      // System.out.println(" COPY "+path+" -> "+(String)value);
+      return Json.createPatchBuilder().copy((String) value, path);
+    } else {
+      throw new IllegalArgumentException(
+          "Argument \"value\" is not an instance of String");
+    }
+  }
+
+  /**
+   * Update patch builder to contain next COPY operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          Source JSON path of COPY operation.
+   * @param value
+   *          Target JSON path of COPY operation. Must be instance of
+   *          {@link String}.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    if (value instanceof String) {
+      // System.out.println(" COPY "+path+" -> "+(String)value);
+      return builder.copy((String) value, path);
+    } else {
+      throw new IllegalArgumentException(
+          "Argument \"value\" is not an instance of String");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationEnum.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationEnum.java
new file mode 100644
index 0000000..82f0c6b
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationEnum.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.JsonPatch;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Test {@link JsonPatch.Operation} enumeration.
+ */
+public class PatchOperationEnum {
+
+  /**
+   * Creates an instance of {@link JsonPatch.Operation} enumeration test.
+   */
+  PatchOperationEnum() {
+    super();
+  }
+
+  /**
+   * Test {@link JsonPatch.Operation} enumeration. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "JsonPatch.Operation enumeration test");
+    System.out.println("JsonPatch.Operation enumeration test");
+    testOperationName(result);
+    testOperationValueOf(result);
+    return result;
+  }
+
+  /**
+   * Test {@link JsonPatch.Operation#fromOperationName(String)} and
+   * {@link JsonPatch.Operation#operationName()} methods.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOperationName(final TestResult result) {
+    System.out.println(" - fromOperationName(String) and operationName(String)");
+    for (final JsonPatch.Operation op : JsonPatch.Operation.values()) {
+      final String opName = op.operationName();
+      final JsonPatch.Operation opOut = JsonPatch.Operation
+          .fromOperationName(opName);
+      final int opNameLen = opName.length();
+      boolean opNameLc = true;
+      for (int i = 0; opNameLc && i < opNameLen; i++) {
+        opNameLc = Character.isLowerCase(opName.charAt(i));
+      }
+      if (!opNameLc) {
+        result.fail("operationName(String)",
+            "Returned value " + opName + " is not lower case String");
+      }
+      if (op != opOut) {
+        result.fail("fromOperationName(String) and operationName(String)",
+            "Returned operation " + opOut.name() + " shall be " + op.name());
+      }
+    }
+  }
+
+  /**
+   * Test {@code JsonPatch.Operation#valueOf(String)} method.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOperationValueOf(final TestResult result) {
+    System.out.println(" - valueOf(String)");
+    for (final JsonPatch.Operation op : JsonPatch.Operation.values()) {
+      final String opName = op.name();
+      final JsonPatch.Operation opOut = JsonPatch.Operation.valueOf(opName);
+      if (op != opOut) {
+        result.fail("valueOf(String)",
+            "Returned operation " + opOut.name() + " shall be " + op.name());
+      }
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationMove.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationMove.java
new file mode 100644
index 0000000..0286f52
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationMove.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatch;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements
+ * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+ * 4.4. move</a>} tests.
+ */
+public class PatchOperationMove extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "MOVE";
+
+  /**
+   * Creates an instance of RFC 6902 replace operation test.
+   */
+  PatchOperationMove() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 move operation");
+    System.out.println("Testing RFC 6902 move operation:");
+    testMoveStringOnSimpleObject(result);
+    testMoveStringOnSimpleArray(result);
+    testMoveStringOnSimpleArray2(result);
+    testMoveIntOnSimpleObject(result);
+    testMoveIntOnSimpleArray(result);
+    testMoveIntOnSimpleArray2(result);
+    testMoveBoolOnSimpleObject(result);
+    testMoveBoolOnSimpleArray(result);
+    testMoveBoolOnSimpleArray2(result);
+    testMoveObjectOnSimpleObject(result);
+    testMoveObjectOnSimpleArray(result);
+    testMoveObjectOnSimpleArray2(result);
+    testMoveStringOnCompoundObject(result);
+    testMoveOfNonExistingLocationInObject(result);
+    testMoveOfNonExistingLocationInArray(result);
+    testMoveVsRemoveAddOnSelfContainedPath(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectMoveStr();
+    simpleOperation(result, in, check, STR_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code String} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createStringArray2R();
+    simpleOperation(result, in, in, "/0", "/0");
+    simpleOperation(result, in, check, "/1", "/0");
+    simpleOperation(result, in, check, "/0", "/1");
+    simpleOperation(result, in, check, "/0", "/-");
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code String} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final JsonArray check = createSimpleStringArray5R();
+    complexOperation(result, in, check, new String[] { "/3", "/0", "/3", "/4" },
+        new String[] { "/1", "/2", "/1", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/0", "/2" },
+        new String[] { "/-", "/2", "/3", "/0" });
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectMoveInt();
+    simpleOperation(result, in, check, INT_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code int} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray in = createIntArray2();
+    final JsonArray check = createIntArray2R();
+    simpleOperation(result, in, in, "/0", "/0");
+    simpleOperation(result, in, check, "/1", "/0");
+    simpleOperation(result, in, check, "/0", "/1");
+    simpleOperation(result, in, check, "/0", "/-");
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code int} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final JsonArray check = createSimpleIntArray5R();
+    complexOperation(result, in, check, new String[] { "/3", "/0", "/3", "/4" },
+        new String[] { "/1", "/2", "/1", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/0", "/2" },
+        new String[] { "/-", "/2", "/3", "/0" });
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectMoveBool();
+    simpleOperation(result, in, check, BOOL_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code boolean} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray in = createBoolArray2();
+    final JsonArray check = createBoolArray2R();
+    simpleOperation(result, in, in, "/0", "/0");
+    simpleOperation(result, in, check, "/1", "/0");
+    simpleOperation(result, in, check, "/0", "/1");
+    simpleOperation(result, in, check, "/0", "/-");
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code boolean} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 5");
+    final JsonArray in = createSimpleBoolArray5();
+    final JsonArray check = createSimpleBoolArray5R();
+    complexOperation(result, in, check, new String[] { "/3", "/0", "/3", "/4" },
+        new String[] { "/1", "/2", "/1", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/0", "/2" },
+        new String[] { "/-", "/2", "/3", "/0" });
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject check = createSimpleObjectMoveObject();
+    simpleOperation(result, in, check, OBJ_PATH, DEF_PATH);
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code JsonObject} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray in = createObjectArray2();
+    final JsonArray check = createObjectArray2R();
+    simpleOperation(result, in, in, "/0", "/0");
+    simpleOperation(result, in, check, "/1", "/0");
+    simpleOperation(result, in, check, "/0", "/1");
+    simpleOperation(result, in, check, "/0", "/-");
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code JsonObject} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final JsonArray check = createSimpleObjectArray5R();
+    complexOperation(result, in, check, new String[] { "/3", "/0", "/3", "/4" },
+        new String[] { "/1", "/2", "/1", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/0", "/2" },
+        new String[] { "/-", "/2", "/3", "/0" });
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for {@code String} on compound JSON object.
+   * Moved value overwrites an existing value.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testMoveStringOnCompoundObject(final TestResult result) {
+    System.out.println(" - for String on compound JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectMoveValue();
+    simpleOperation(result, in, check, DEF_PATH, DEF_OBJ_PATH + DEF_PATH);
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test RFC 6902 MOVE operation for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.4. move</a>} defines:<br>
+   * The "from" location MUST exist for the operation to be successful.
+   */
+  private void testMoveOfNonExistingLocationInObject(final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final Object[] values = new Object[] { OBJ_PATH, BOOL_PATH, INT_PATH,
+        STR_PATH };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, objsIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.4. move</a>} defines:<br>
+   * The "from" location MUST exist for the operation to be successful.
+   */
+  private void testMoveOfNonExistingLocationInArray(final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2() };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    final Object[] values = new Object[] { "/0", "/1", "/2", "/5", "/1" };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, arraysIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation for moving existing path into path containing
+   * source path as a prefix.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.4. move</a>} defines:<br>
+   * This operation is functionally identical to a "remove" operation on the
+   * "from" location, followed immediately by an "add" operation at the target
+   * location with the value that was just removed.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-7">RFC 6901: 7.
+   * Error Handling</a>} defines:<br>
+   * This specification does not define how errors are handled. An application
+   * of JSON Pointer SHOULD specify the impact and handling of each type of
+   * error.<br>
+   * For example, some applications might stop pointer processing upon an error,
+   * while others may attempt to recover from missing values by inserting
+   * default ones.<br>
+   * This means, that such an operation may fail on non existing target "path"
+   * after ADD operation, but also missing pointer error recovery may take care
+   * of creating missing "path" elements. In both cases MOVE and sequence of
+   * REMOVE and ADD operations MUST produce the same result.
+   */
+  private void testMoveVsRemoveAddOnSelfContainedPath(final TestResult result) {
+    System.out.println(" - for moving JsonObject under itself");
+    final JsonObject in = createCompoundObject();
+    final String targetPath = DEF_OBJ_PATH + DEF_PATH;
+    final JsonPointer ptr = Json.createPointer(DEF_OBJ_PATH);
+    final JsonValue value = ptr.getValue(in);
+    final JsonPatchBuilder moveBuilder = Json.createPatchBuilder()
+        .move(targetPath, DEF_OBJ_PATH);
+    final JsonPatchBuilder remAddBuilder = Json.createPatchBuilder()
+        .remove(DEF_OBJ_PATH).add(targetPath, value);
+    final JsonPatch movePatch = moveBuilder.build();
+    final JsonPatch remAddPatch = remAddBuilder.build();
+    // Check REMOVE and ADD sequence first.
+    JsonObject remAddOut;
+    try {
+
+      remAddOut = remAddPatch.apply(in);
+      System.out.println("   REMOVE and ADD passed");
+    } catch (JsonException e) {
+      remAddOut = null;
+      System.out.println("   REMOVE and ADD failed: " + e.getMessage());
+    }
+    // Check MOVE second
+    JsonObject moveOut;
+    try {
+      moveOut = movePatch.apply(in);
+      System.out.println("   MOVE passed");
+    } catch (JsonException e) {
+      moveOut = null;
+      System.out.println("   MOVE failed: " + e.getMessage());
+    }
+    // Results evaluation
+    if (remAddOut != null) {
+      // Both output values are not null: Compare them
+      if (moveOut != null) {
+        if (!assertEquals(remAddOut, moveOut)) {
+          result.fail("MOVE vs REMOVE and ADD",
+              "Returned values are not equal");
+        }
+        // REMOVE and ADD output is not null but MOVE output is null
+      } else {
+        result.fail("MOVE vs REMOVE and ADD",
+            "REMOVE and ADD failed but MOVE dit not");
+      }
+    } else {
+      // REMOVE and ADD output is null but MOVE output is not null
+      if (moveOut != null) {
+        result.fail("MOVE vs REMOVE and ADD",
+            "MOVE failed but REMOVE and ADD dit not");
+      }
+      // else: Both output values are null: both patch operations failed -> test
+      // passed
+    }
+  }
+
+  /**
+   * Tested operation name {@code "MOVE"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain MOVE operation to be
+   * applied.
+   * 
+   * @param path
+   *          Source JSON path of MOVE operation.
+   * @param value
+   *          Target JSON path of MOVE operation. Must be instance of
+   *          {@link String}.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    if (value instanceof String) {
+      // System.out.println(" MOVE "+path+" -> "+(String)value);
+      return Json.createPatchBuilder().move((String) value, path);
+    } else {
+      throw new IllegalArgumentException(
+          "Argument \"value\" is not an instance of String");
+    }
+  }
+
+  /**
+   * Update patch builder to contain next MOVE operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          Source JSON path of MOVE operation.
+   * @param value
+   *          Target JSON path of MOVE operation. Must be instance of
+   *          {@link String}.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    if (value instanceof String) {
+      // System.out.println(" MOVE "+path+" -> "+(String)value);
+      return builder.move((String) value, path);
+    } else {
+      throw new IllegalArgumentException(
+          "Argument \"value\" is not an instance of String");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationRemove.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationRemove.java
new file mode 100644
index 0000000..a19b362
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationRemove.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/*
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902: 4.2. remove</a>}
+ * tests.
+ */
+public class PatchOperationRemove extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "REMOVE";
+
+  /**
+   * Creates an instance of RFC 6902 remove operation test.
+   */
+  PatchOperationRemove() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 remove operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 remove operation");
+    System.out.println("Testing RFC 6902 remove operation:");
+    testRemoveStringOnEmptyObject(result);
+    testRemoveStringOnEmptyArray(result);
+    testRemoveStringOnSimpleObject(result);
+    testRemoveStringOnSimpleArray(result);
+    testRemoveStringOnSimpleArray2(result);
+    testRemoveIntOnEmptyObject(result);
+    testRemoveIntOnEmptyArray(result);
+    testRemoveIntOnSimpleObject(result);
+    testRemoveIntOnSimpleArray(result);
+    testRemoveIntOnSimpleArray2(result);
+    testRemoveBoolOnEmptyObject(result);
+    testRemoveBoolOnEmptyArray(result);
+    testRemoveBoolOnSimpleObject(result);
+    testRemoveBoolOnSimpleArray(result);
+    testRemoveBoolOnSimpleArray2(result);
+    testRemoveObjectOnEmptyObject(result);
+    testRemoveObjectOnEmptyArray(result);
+    testRemoveObjectOnSimpleObject(result);
+    testRemoveObjectOnSimpleArray(result);
+    testRemoveObjectOnSimpleArray2(result);
+    testRemoveFromNonExistingLocationInObject(result);
+    testRemoveFromNonExistingLocationInArray(result);
+    return result;
+  }
+
+  /**
+   * Test pointer remove operation for {@code String} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String to produce empty JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createEmptyObject();
+    simpleOperation(result, in, check, STR_PATH, null);
+  }
+
+  /**
+   * Test pointer remove operation for {@code String} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnEmptyArray(final TestResult result) {
+    System.out.println(" - for String to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithStr();
+    final JsonArray check = createEmptyArray();
+    simpleOperation(result, in, check, "/0", null);
+  }
+
+  /**
+   * Test pointer remove operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectWithStr();
+    final JsonObject check = createSimpleObject();
+    simpleOperation(result, in, check, STR_PATH, null);
+  }
+
+  /**
+   * Test pointer remove operation for {@code String} on simple JSON array of
+   * size 2. Using index {@code 0} to remove {@code String} before another
+   * existing element and index {@code 1} to remove {@code String} after another
+   * existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleStringArrayWithStrBefore();
+    final JsonArray inAfter = createSimpleStringArrayWithStrAfter();
+    final JsonArray check = createStringArray1();
+    simpleOperation(result, inBefore, check, "/0", null);
+    simpleOperation(result, inAfter, check, "/1", null);
+  }
+
+  /**
+   * Test pointer REMOVE for {@code String} on simple JSON array of size 5.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Removing {@code String} at the end, at the middle and at the beginning
+   * of this array.
+   * <li>Removing {@code String} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final JsonArray check = createStringArray2();
+    complexOperation(result, in, check, new String[] { "/4", "/2", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/2" });
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} to produce empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int to produce empty JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createEmptyObject();
+    simpleOperation(result, in, check, INT_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} to produce empty JSON array.
+   * Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnEmptyArray(final TestResult result) {
+    System.out.println(" - for int to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithInt();
+    final JsonArray check = createEmptyArray();
+    simpleOperation(result, in, check, "/0", null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectWithInt();
+    final JsonObject check = createSimpleObject();
+    simpleOperation(result, in, check, INT_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} on simple JSON array of size
+   * 2. Using index {@code 0} to remove {@code int} before another existing
+   * element and index {@code 1} to remove {@code int} after another existing
+   * element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleIntArrayWithIntBefore();
+    final JsonArray inAfter = createSimpleIntArrayWithIntAfter();
+    final JsonArray check = createIntArray1();
+    simpleOperation(result, inBefore, check, "/0", null);
+    simpleOperation(result, inAfter, check, "/1", null);
+  }
+
+  /**
+   * Test pointer REMOVE for {@code int} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code int} at the end, at the middle and at the beginning of
+   * this array.
+   * <li>Removing {@code int} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final JsonArray check = createIntArray2();
+    complexOperation(result, in, check, new String[] { "/4", "/2", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/2" });
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean to produce empty JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createEmptyObject();
+    simpleOperation(result, in, check, BOOL_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnEmptyArray(final TestResult result) {
+    System.out.println(" - for boolean to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithBool();
+    final JsonArray check = createEmptyArray();
+    simpleOperation(result, in, check, "/0", null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectWithBool();
+    final JsonObject check = createSimpleObject();
+    simpleOperation(result, in, check, BOOL_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} on simple JSON array of
+   * size 2. Using index {@code 0} to remove {@code boolean} before another
+   * existing element and index {@code 1} to remove {@code boolean} after
+   * another existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleBoolArrayWithBoolBefore();
+    final JsonArray inAfter = createSimpleBoolArrayWithBoolAfter();
+    final JsonArray check = createBoolArray1();
+    simpleOperation(result, inBefore, check, "/0", null);
+    simpleOperation(result, inAfter, check, "/1", null);
+  }
+
+  /**
+   * Test pointer REMOVE for {@code boolean} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code boolean} at the end, at the middle and at the beginning
+   * of this array.
+   * <li>Removing {@code boolean} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 5");
+    final JsonArray in = createSimpleBoolArray5();
+    final JsonArray check = createBoolArray2();
+    complexOperation(result, in, check, new String[] { "/4", "/2", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/2" });
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject to produce empty JSON object");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject check = createEmptyObject();
+    simpleOperation(result, in, check, OBJ_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnEmptyArray(final TestResult result) {
+    System.out.println(" - for JsonObject to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithObject();
+    final JsonArray check = createEmptyArray();
+    simpleOperation(result, in, check, "/0", null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObjectWithObject();
+    final JsonObject check = createCompoundObject();
+    simpleOperation(result, in, check, OBJ_PATH, null);
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} on simple JSON array
+   * of size 2. Using index {@code 0} to remove {@code JsonObject} before
+   * another existing element and index {@code 1} to remove {@code JsonObject}
+   * after another existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleObjectArrayWithObjectBefore();
+    final JsonArray inAfter = createSimpleObjectArrayWithObjectAfter();
+    final JsonArray check = createObjectArray1();
+    simpleOperation(result, inBefore, check, "/0", null);
+    simpleOperation(result, inAfter, check, "/1", null);
+  }
+
+  /**
+   * Test pointer REMOVE for {@code JsonObject} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code JsonObject} at the end, at the middle and at the
+   * beginning of this array.
+   * <li>Removing {@code JsonObject} at the beginning, in the middle and at the
+   * end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final JsonArray check = createObjectArray2();
+    complexOperation(result, in, check, new String[] { "/4", "/2", "/0" });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/2" });
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test pointer REMOVE for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testRemoveFromNonExistingLocationInObject(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, objsIn[i], paths[j], null);
+      }
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testRemoveFromNonExistingLocationInArray(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2()
+
+    };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, arraysIn[i], paths[j], null);
+      }
+    }
+  }
+
+  /**
+   * Tested operation name {@code "REMOVE"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain REMOVE operation to be
+   * applied.
+   * 
+   * @param path
+   *          JSON path of value to removed.
+   * @param value
+   *          Not used for REMOVE operation.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    return Json.createPatchBuilder().remove(path);
+  }
+
+  /**
+   * Update patch builder to contain next REMOVE operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          JSON path of value to removed.
+   * @param value
+   *          Not used for REMOVE operation.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    return builder.remove(path);
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationReplace.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationReplace.java
new file mode 100644
index 0000000..66190c5
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationReplace.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements
+ * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+ * 4.3. replace</a>} tests.
+ */
+public class PatchOperationReplace extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "REPLACE";
+
+  /**
+   * Creates an instance of RFC 6902 replace operation test.
+   */
+  PatchOperationReplace() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 replace operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 replace operation");
+    System.out.println("Testing RFC 6902 replace operation:");
+    testReplaceStringOnSimpleObject(result);
+    testReplaceStringOnSimpleArray(result);
+    testReplaceStringOnSimpleArray2(result);
+    testReplaceIntOnSimpleObject(result);
+    testReplaceIntOnSimpleArray(result);
+    testReplaceIntOnSimpleArray2(result);
+    testReplaceBoolOnSimpleObject(result);
+    testReplaceBoolOnSimpleArray(result);
+    testReplaceBoolOnSimpleArray2(result);
+    testReplaceObjectOnCompoundObject(result);
+    testReplaceObjectOnSimpleArray(result);
+    testReplaceObjectOnSimpleArray2(result);
+    testReplaceOfNonExistingLocationInObject(result);
+    testReplaceOfNonExistingLocationInArray(result);
+    return result;
+  }
+
+  /**
+   * Test pointer replace operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectReplaceStr();
+    simpleOperation(result, in, check, STR_PATH, STR_VALUE2);
+  }
+
+  /**
+   * Test pointer replace operation for {@code String} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 1");
+    final JsonArray in = createStringArray1();
+    final JsonArray check = createSimpleStringArrayReplaceStr();
+    simpleOperation(result, in, check, "/0", STR_VALUE);
+  }
+
+  /**
+   * Test pointer replace operation for {@code String} on simple JSON array of
+   * size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code String} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code String} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final JsonArray check = createSimpleStringArray5R();
+    complexOperation(result, in, check, new String[] { "/4", "/3", "/1", "/0" },
+        new String[] { STR_VALUE_1, STR_VALUE_2, STR_VALUE_4, STR_VALUE_5 });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/3", "/4" },
+        new String[] { STR_VALUE_5, STR_VALUE_4, STR_VALUE_2, STR_VALUE_1 });
+  }
+
+  /**
+   * Test pointer replace operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectReplaceInt();
+    simpleOperation(result, in, check, INT_PATH, INT_VALUE2);
+  }
+
+  /**
+   * Test pointer replace operation for {@code int} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 1");
+    final JsonArray in = createIntArray1();
+    final JsonArray check = createSimpleIntArrayReplaceInt();
+    simpleOperation(result, in, check, "/0", INT_VALUE);
+  }
+
+  /**
+   * Test pointer replace operation for {@code int} on simple JSON array of size
+   * 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code int} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code int} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final JsonArray check = createSimpleIntArray5R();
+    complexOperation(result, in, check, new String[] { "/4", "/3", "/1", "/0" },
+        new Integer[] { INT_VALUE_1, INT_VALUE_2, INT_VALUE_4, INT_VALUE_5 });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/3", "/4" },
+        new Integer[] { INT_VALUE_5, INT_VALUE_4, INT_VALUE_2, INT_VALUE_1 });
+  }
+
+  /**
+   * Test pointer replace operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectReplaceBool();
+    simpleOperation(result, in, check, BOOL_PATH, BOOL_VALUE2);
+  }
+
+  /**
+   * Test pointer replace operation for {@code boolean} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 1");
+    final JsonArray in = createBoolArray1();
+    final JsonArray check = createSimpleBoolArrayReplaceBool();
+    simpleOperation(result, in, check, "/0", BOOL_FALSE);
+  }
+
+  /**
+   * Test pointer replace operation for {@code boolean} on simple JSON array of
+   * size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code boolean} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code boolean} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 5");
+    final JsonArray in = createSimpleBoolArray5();
+    final JsonArray check = createSimpleBoolArray5R();
+    complexOperation(result, in, check, new String[] { "/4", "/3", "/1", "/0" },
+        new Boolean[] { BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, BOOL_TRUE });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/3", "/4" },
+        new Boolean[] { BOOL_TRUE, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE });
+  }
+
+  /**
+   * Test pointer replace operation for {@code JsonObject} on compound JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnCompoundObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObjectWithObject();
+    final JsonObject check = createCompoundObjectReplaceObject();
+    simpleOperation(result, in, check, OBJ_PATH, OBJ_VALUE2);
+  }
+
+  /**
+   * Test pointer replace operation for {@code JsonObject} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 1");
+    final JsonArray in = createObjectArray1();
+    final JsonArray check = createSimpleObjectArrayReplaceObject();
+    simpleOperation(result, in, check, "/0", OBJ_VALUE);
+  }
+
+  /**
+   * Test pointer replace operation for {@code JsonObject} on simple JSON array
+   * of size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code JsonObject} items from the end to the beginning of
+   * this array.
+   * <li>Replacing {@code JsonObject} from the beginning to the end of this
+   * array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final JsonArray check = createSimpleObjectArray5R();
+    complexOperation(result, in, check, new String[] { "/4", "/3", "/1", "/0" },
+        new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_2, OBJ_VALUE_4,
+            OBJ_VALUE_5 });
+    complexOperation(result, in, check, new String[] { "/0", "/1", "/3", "/4" },
+        new JsonObject[] { OBJ_VALUE_5, OBJ_VALUE_4, OBJ_VALUE_2,
+            OBJ_VALUE_1 });
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test pointer replace for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+   * 4.3. replace</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testReplaceOfNonExistingLocationInObject(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final Object[] values = new Object[] { STR_VALUE, INT_VALUE, BOOL_VALUE,
+        OBJ_VALUE };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, objsIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Test pointer replace for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+   * 4.3. replace</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testReplaceOfNonExistingLocationInArray(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2() };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    final Object[] values = new Object[] { STR_VALUE, STR_VALUE, INT_VALUE,
+        BOOL_VALUE, OBJ_VALUE };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        simpleOperationFail(result, arraysIn[i], paths[j], values[i]);
+      }
+    }
+  }
+
+  /**
+   * Tested operation name {@code "MOVE"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain REPLACE operation to be
+   * applied.
+   * 
+   * @param path
+   *          JSON path of value to be replaced.
+   * @param value
+   *          Value to replace previous one.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    return builderReplace(Json.createPatchBuilder(), path, value);
+  }
+
+  /**
+   * Update patch builder to contain next REPLACE operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          JSON path of value to be replaced.
+   * @param value
+   *          Value to replace previous one.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    return builderReplace(builder, path, value);
+  }
+
+  /**
+   * Add REPLACE {@code value} at {@code path} operation to provided JSON patch
+   * builder.
+   * 
+   * @param builder
+   *          Target JSON patch builder.
+   * @param path
+   *          JSON path of value to be replaced.
+   * @param value
+   *          Value to be replaced at given JSON path.
+   * @return JSON patch builder containing new {@code value} at {@code path}
+   *         replaced.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  private static JsonPatchBuilder builderReplace(final JsonPatchBuilder builder,
+      final String path, final Object value) {
+    switch (JsonValueType.getType(value.getClass())) {
+    case String:
+      return builder.replace(path, (String) value);
+    case Integer:
+      return builder.replace(path, ((Integer) value).intValue());
+    case Boolean:
+      return builder.replace(path, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.replace(path, (JsonValue) value);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationTest.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationTest.java
new file mode 100644
index 0000000..14ac33b
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchOperationTest.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.JsonValueType;
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPatchBuilder;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ * <p>
+ * Implements
+ * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.6">RFC 6902:
+ * 4.6. test</a>} tests.
+ */
+public class PatchOperationTest extends CommonOperation {
+
+  /** Tested operation name. */
+  private final String OPERATION = "TEST";
+
+  /**
+   * Creates an instance of RFC 6902 replace operation test.
+   */
+  PatchOperationTest() {
+    super();
+  }
+
+  /**
+   * Test RFC 6902 MOVE operation. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6902 test operation");
+    System.out.println("Testing RFC 6902 test operation:");
+    testOnEmptyObject(result);
+    testOnEmptyArray(result);
+    testOnSimpleObject(result);
+    testOnSimpleStringArray(result);
+    testOnSimpleIntArray(result);
+    testOnSimpleBoolArray(result);
+    testOnSimpleObjectArray(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 6902 TEST operation on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnEmptyObject(final TestResult result) {
+    System.out.println(" - on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final Object[] values = new Object[] { STR_VALUE, INT_VALUE, BOOL_VALUE,
+        OBJ_VALUE };
+    // Go trough all values.
+    for (int i = 0; i < values.length; i++) {
+      // Go trough all paths.
+      for (int j = 0; j < paths.length; j++) {
+        // Everything shall fail on empty object.
+        simpleOperationFail(result, in, paths[j], values[i]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Test RFC 6902 TEST operation on empty JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnEmptyArray(final TestResult result) {
+    System.out.println(" - on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final String[] paths = new String[] { "/-1", "/0", "/1", "/2", "/3", "/4",
+        "/5", "/-" };
+    final Object[] values = new Object[] { STR_VALUE, INT_VALUE, BOOL_VALUE,
+        OBJ_VALUE };
+    // Go trough all values.
+    for (int i = 0; i < values.length; i++) {
+      // Go trough all paths.
+      for (int j = 0; j < paths.length; j++) {
+        // Everything shall fail on empty object.
+        simpleOperationFail(result, in, paths[j], values[i]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Test RFC 6902 TEST on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnSimpleObject(final TestResult result) {
+    System.out.println(" - on simple JSON object");
+    final JsonObject[] in = new JsonObject[] { createSimpleObjectStr(),
+        createSimpleObjectInt(), createSimpleObjectBool(),
+        createSimpleObjectObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final Object[] values = new Object[] { STR_VALUE, INT_VALUE, BOOL_VALUE,
+        OBJ_VALUE };
+    // go trough all source objects.
+    for (int o = 0; o < in.length; o++) {
+      // Go trough all values.
+      for (int i = 0; i < values.length; i++) {
+        // Go trough all paths.
+        for (int j = 0; j < paths.length; j++) {
+          if (o == i && o == j) {
+            // TEST must pass for matching JsonObject, path and value
+            simpleOperation(result, in[o], null, paths[j], values[i]);
+          } else {
+            // TEST must fail for non matching JsonObject, path and value
+            simpleOperationFail(result, in[o], paths[j], values[i]);
+          }
+        }
+      }
+      // Whole document should pass on itself.
+      simpleOperation(result, in[o], null, "", in[o]);
+    }
+  }
+
+  /**
+   * Test RFC 6902 TEST on simple JSON array of {@code String}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnSimpleStringArray(final TestResult result) {
+    System.out.println(" - on simple JSON String array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final String[] indexes = new String[] { "/-1", "/-", STR_PATH };
+    final String[] values = new String[] { STR_VALUE_1, STR_VALUE_2,
+        STR_VALUE_3, STR_VALUE_4, STR_VALUE_5 };
+    // Go trough all array indexes.
+    for (int i = 0; i <= 5; i++) {
+      final String path = arrayPtr("/", i);
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        if (i == v) {
+          // TEST must pass for matching index and value.
+          simpleOperation(result, in, null, path, values[v]);
+        } else {
+          // TEST must fail for non matching index and value.
+          simpleOperationFail(result, in, path, values[v]);
+        }
+      }
+    }
+    // Go trough all invalid indexes
+    for (int i = 0; i < indexes.length; i++) {
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        // TEST must fail for non matching index and value.
+        simpleOperationFail(result, in, indexes[i], values[v]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Test RFC 6902 TEST on simple JSON array of {@code int}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnSimpleIntArray(final TestResult result) {
+    System.out.println(" - on simple JSON int array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final String[] indexes = new String[] { "/-1", "/-", INT_PATH };
+    final int[] values = new int[] { INT_VALUE_1, INT_VALUE_2, INT_VALUE_3,
+        INT_VALUE_4, INT_VALUE_5 };
+    // Go trough all array indexes.
+    for (int i = 0; i <= 5; i++) {
+      final String path = arrayPtr("/", i);
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        if (i == v) {
+          // TEST must pass for matching index and value.
+          simpleOperation(result, in, null, path, values[v]);
+        } else {
+          // TEST must fail for non matching index and value.
+          simpleOperationFail(result, in, path, values[v]);
+        }
+      }
+    }
+    // Go trough all invalid indexes
+    for (int i = 0; i < indexes.length; i++) {
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        // TEST must fail for non matching index and value.
+        simpleOperationFail(result, in, indexes[i], values[v]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Test RFC 6902 TEST on simple JSON array of {@code boolean}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnSimpleBoolArray(final TestResult result) {
+    System.out.println(" - on simple JSON boolean array of size 2");
+    final JsonArray in = createBoolArray2();
+    final String[] indexes = new String[] { "/-1", "/-", BOOL_PATH };
+    final boolean[] values = new boolean[] { BOOL_TRUE, BOOL_FALSE };
+    // Go trough all array indexes.
+    for (int i = 0; i <= 2; i++) {
+      final String path = arrayPtr("/", i);
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        if (i == v) {
+          // TEST must pass for matching index and value.
+          simpleOperation(result, in, null, path, values[v]);
+        } else {
+          // TEST must fail for non matching index and value.
+          simpleOperationFail(result, in, path, values[v]);
+        }
+      }
+    }
+    // Go trough all invalid indexes
+    for (int i = 0; i < indexes.length; i++) {
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        // TEST must fail for non matching index and value.
+        simpleOperationFail(result, in, indexes[i], values[v]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Test RFC 6902 TEST on simple JSON array of {@code JsonObject}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testOnSimpleObjectArray(final TestResult result) {
+    System.out.println(" - on simple JSON JsonObject array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final String[] indexes = new String[] { "/-1", "/-", OBJ_PATH };
+    final JsonObject[] values = new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_2,
+        OBJ_VALUE_3, OBJ_VALUE_4, OBJ_VALUE_5 };
+    // Go trough all array indexes.
+    for (int i = 0; i <= 5; i++) {
+      final String path = arrayPtr("/", i);
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        if (i == v) {
+          // TEST must pass for matching index and value.
+          simpleOperation(result, in, null, path, values[v]);
+        } else {
+          // TEST must fail for non matching index and value.
+          simpleOperationFail(result, in, path, values[v]);
+        }
+      }
+    }
+    // Go trough all invalid indexes
+    for (int i = 0; i < indexes.length; i++) {
+      // Go trough all values.
+      for (int v = 0; v < values.length; v++) {
+        // TEST must fail for non matching index and value.
+        simpleOperationFail(result, in, indexes[i], values[v]);
+      }
+    }
+    // Whole document should pass on itself.
+    simpleOperation(result, in, null, "", in);
+  }
+
+  /**
+   * Build JSON array pointer for given prefix and index.
+   * 
+   * @param prefix
+   *          JSON array pointer prefix.
+   * @param index
+   *          JSON array pointer index.
+   * @return JSON array pointer.
+   */
+  private static String arrayPtr(final String prefix, final int index) {
+    final int prefixLen = prefix != null ? prefix.length() : 0;
+    final String indexStr = Integer.toString(index);
+    final StringBuilder sb = new StringBuilder(prefixLen + indexStr.length());
+    if (prefixLen > 0) {
+      sb.append(prefix);
+    }
+    sb.append(indexStr);
+    return sb.toString();
+  }
+
+  /**
+   * Tested operation name {@code "TEST"}.
+   * 
+   * @return Operation name to be used in logs.
+   */
+  @Override
+  protected String operationName() {
+    return OPERATION;
+  }
+
+  /**
+   * Create and initialize patch builder to contain TEST operation to be
+   * applied.
+   * 
+   * @param path
+   *          JSON path of value to be tested.
+   * @param value
+   *          Value to be compared against value on specified path.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder createOperationBuilder(final String path,
+      final Object value) {
+    return builderTest(Json.createPatchBuilder(), path, value);
+  }
+
+  /**
+   * Update patch builder to contain next TEST operation to be applied.
+   * 
+   * @param builder
+   *          JSON patch builder to update.
+   * @param path
+   *          JSON path of value to be tested.
+   * @param value
+   *          Value to be compared against value on specified path.
+   * @return Patch builder containing operation to be applied.
+   */
+  @Override
+  protected JsonPatchBuilder updateOperationBuilder(
+      final JsonPatchBuilder builder, final String path, final Object value) {
+    return builderTest(builder, path, value);
+  }
+
+  /**
+   * Add TEST {@code value} at {@code path} operation to provided JSON patch
+   * builder.
+   * 
+   * @param builder
+   *          Target JSON patch builder.
+   * @param path
+   *          JSON path of value to be tested.
+   * @param value
+   *          Value to be compared against value on specified path.
+   * @return JSON patch builder containing new TEST operation.
+   */
+  @SuppressWarnings("UnnecessaryUnboxing")
+  private static JsonPatchBuilder builderTest(final JsonPatchBuilder builder,
+      final String path, final Object value) {
+    switch (JsonValueType.getType(value.getClass())) {
+    case String:
+      return builder.test(path, (String) value);
+    case Integer:
+      return builder.test(path, ((Integer) value).intValue());
+    case Boolean:
+      return builder.test(path, ((Boolean) value).booleanValue());
+    case JsonValue:
+      return builder.test(path, (JsonValue) value);
+    default:
+      throw new IllegalArgumentException(
+          "Value does not match known JSON value type");
+    }
+  }
+
+  /**
+   * Operation result check.
+   * 
+   * @param check
+   *          Expected modified JSON value.
+   * @param out
+   *          Operation output.
+   * @return Value of {@code true} if operation passed or {@code false}
+   *         otherwise.
+   */
+  @Override
+  protected boolean operationFailed(final JsonValue check,
+      final JsonValue out) {
+    return out == null;
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchTests.java
new file mode 100644
index 0000000..1c66c45
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/patchtests/PatchTests.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.patchtests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// $Id$
+/*
+ * RFC 6902: JavaScript Object Notation (JSON) Patch compatibility tests.<br>
+ * {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}.
+ */
+@RunWith(Arquillian.class)
+public class PatchTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, PatchTests.class.getPackage().getName());
+    }
+  /**
+   * Test {@link JsonPatch} factory methods added in JSON-P 1.1.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonCreatePatch11Test
+   *
+   * @assertion_ids: JSONP:JAVADOC:574; JSONP:JAVADOC:579; JSONP:JAVADOC:581;
+   *                 JSONP:JAVADOC:653; JSONP:JAVADOC:658; JSONP:JAVADOC:660;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:621;
+   *
+   * @test_Strategy: Tests JsonPatch API factory methods added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonCreatePatch11Test() throws Fault {
+    PatchCreate createTest = new PatchCreate();
+    final TestResult result = createTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test {@code JsonPatch.Operation} enumeration added in JSON-P 1.1.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonJsonPatchOperation11Test
+   *
+   * @assertion_ids: JSONP:JAVADOC:622; JSONP:JAVADOC:623; JSONP:JAVADOC:624;
+   *                 JSONP:JAVADOC:625;
+   *
+   * @test_Strategy: Tests JsonPatch.Operation enumeration added in JSON-P 1.1.
+   */
+  @Test
+  public void jsonJsonPatchOperation11Test() throws Fault {
+    PatchOperationEnum enumTest = new PatchOperationEnum();
+    final TestResult result = enumTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on add operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonPatchAddTest
+   * @assertion_ids: JSONP:JAVADOC:626; JSONP:JAVADOC:627; JSONP:JAVADOC:628;
+   *                 JSONP:JAVADOC:629; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of add operation.
+   */
+  @Test
+  public void jsonPatchAddTest() throws Fault {
+    PatchOperationAdd addTest = new PatchOperationAdd();
+    final TestResult result = addTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on remove operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonPatchRemoveTest
+   * @assertion_ids: JSONP:JAVADOC:633; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of remove operation.
+   */
+  @Test
+  public void jsonPatchRemoveTest() throws Fault {
+    PatchOperationRemove removeTest = new PatchOperationRemove();
+    final TestResult result = removeTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on replace operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+   * 4.3. replace</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonPatchReplaceTest
+   * @assertion_ids: JSONP:JAVADOC:634; JSONP:JAVADOC:635; JSONP:JAVADOC:636;
+   *                 JSONP:JAVADOC:637; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of replace operation.
+   */
+  @Test
+  public void jsonPatchReplaceTest() throws Fault {
+    PatchOperationReplace replaceTest = new PatchOperationReplace();
+    final TestResult result = replaceTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on move operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.4">RFC 6902:
+   * 4.4. move</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonPatchMoveTest
+   * @assertion_ids: JSONP:JAVADOC:632; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of move operation.
+   */
+  @Test
+  public void jsonPatchMoveTest() throws Fault {
+    PatchOperationMove moveTest = new PatchOperationMove();
+    final TestResult result = moveTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on copy operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.5">RFC 6902:
+   * 4.5. copy</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonPatchCopyTest
+   * @assertion_ids: JSONP:JAVADOC:631; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of copy operation.
+   */
+  @Test
+  public void jsonPatchCopyTest() throws Fault {
+    PatchOperationCopy copyTest = new PatchOperationCopy();
+    final TestResult result = copyTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSONP API response on test operation.<br>
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.6">RFC 6902:
+   * 4.6. test</a>}.
+   *
+   * @throws Fault
+   *           when this test failed.
+   *
+   * @testName: jsonPatchTestTest
+   * @assertion_ids: JSONP:JAVADOC:638; JSONP:JAVADOC:639; JSONP:JAVADOC:640;
+   *                 JSONP:JAVADOC:641; JSONP:JAVADOC:580; JSONP:JAVADOC:659;
+   *                 JSONP:JAVADOC:620; JSONP:JAVADOC:630;
+   * @test_Strategy: Test API response on various usages of test operation.
+   */
+  @Test
+  public void jsonPatchTestTest() throws Fault {
+    PatchOperationTest testTest = new PatchOperationTest();
+    final TestResult result = testTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerAdd.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerAdd.java
new file mode 100644
index 0000000..7b0d9d4
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerAdd.java
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.pointertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonStructure;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: pointer
+ * usage for {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}
+ * add operation tests.<br>
+ */
+public class PointerAdd {
+
+  /**
+   * Creates an instance of RFC 6901 pointer instance usage for RFC 6902 add
+   * operation tests.
+   */
+  PointerAdd() {
+    super();
+  }
+
+  /**
+   * Test RFC 6901 pointer instance usage for RFC 6902 add operation. Suite
+   * entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 6901 pointer usage for RFC 6902 add operation");
+    System.out.println("Testing RFC 6901 pointer usage for RFC 6902 add operation");
+    testAddStringOnEmptyObject(result);
+    testAddStringOnEmptyArray(result);
+    testAddStringOnSimpleObject(result);
+    testAddStringOnSimpleArray(result);
+    testAddStringOnSimpleArray2(result);
+    testAddIntOnEmptyObject(result);
+    testAddIntOnEmptyArray(result);
+    testAddIntOnSimpleObject(result);
+    testAddIntOnSimpleArray(result);
+    testAddIntOnSimpleArray2(result);
+    testAddBoolOnEmptyObject(result);
+    testAddBoolOnEmptyArray(result);
+    testAddBoolOnSimpleObject(result);
+    testAddBoolOnSimpleArray(result);
+    testAddBoolOnSimpleArray2(result);
+    testAddObjectOnEmptyObject(result);
+    testAddObjectOnEmptyArray(result);
+    testAddObjectOnSimpleObject(result);
+    testAddObjectOnSimpleArray(result);
+    testAddObjectOnSimpleArray2(result);
+    testAddArrayToReplaceObject(result);
+    testAddArrayToReplaceDocument(result);
+    testAddStringArrayToStringArray(result);
+    testAddStringToNonExistingObject(result);
+    return result;
+  }
+
+  /**
+   * Test pointer ADD operation for {@code String} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectStr();
+    final JsonPointer ptr = Json.createPointer(STR_PATH);
+    final JsonObject out = ptr.add(in, Json.createValue(STR_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + STR_PATH + "\" ADD \""
+          + STR_VALUE + "\" failed on empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code String} on empty JSON array. Only
+   * allowed index for empty array is {@code 0} and {@code -}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnEmptyArray(final TestResult result) {
+    System.out.println(" - for String on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithStr();
+    final JsonPointer[] ptrs = new JsonPointer[] { Json.createPointer("/0"),
+        Json.createPointer("/-") };
+    for (final JsonPointer ptr : ptrs) {
+      final JsonArray out = ptr.add(in, Json.createValue(STR_VALUE));
+      if (!assertEquals(check, out)) {
+        result.fail("Pointer ADD operation", "Pointer \"" + ptr + "\" ADD \""
+            + STR_VALUE + "\" failed on empty JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithStr();
+    final JsonPointer ptr = Json.createPointer(STR_PATH);
+    final JsonObject out = ptr.add(in, Json.createValue(STR_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + STR_PATH + "\" ADD \""
+          + STR_VALUE + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code String} on simple JSON array of size
+   * 1. Using index {@code 0} to add {@code String} before already existing
+   * element and indexes {@code 1} and {@code -} to add {@code String} after
+   * already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 1");
+    final JsonArray in = createStringArray1();
+    final JsonArray checkBefore = createSimpleStringArrayWithStrBefore();
+    final JsonArray checkAfter = createSimpleStringArrayWithStrAfter();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer[] ptrsAfter = new JsonPointer[] {
+        Json.createPointer("/1"), Json.createPointer("/-") };
+    final JsonArray outBefore = ptrBefore.add(in, Json.createValue(STR_VALUE));
+    if (!assertEquals(checkBefore, outBefore)) {
+      result.fail("Pointer ADD operation", "Pointer \"/0\" ADD \"" + STR_VALUE
+          + "\" failed on simple JSON array");
+    }
+    for (final JsonPointer ptrAfter : ptrsAfter) {
+      final JsonArray outAfter = ptrAfter.add(in, Json.createValue(STR_VALUE));
+      if (!assertEquals(checkAfter, outAfter)) {
+        result.fail("Pointer ADD operation", "Pointer \"/1\" ADD \"" + STR_VALUE
+            + "\" failed on simple JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code String} on simple JSON array of size
+   * 2. Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code String} at the end, in the middle and at the beginning of
+   * this array.
+   * <li>Adding {@code String} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createSimpleStringArray5();
+    verifyAddValues(result, in, check, new String[] { "/2", "/1", "/0" },
+        new String[] { STR_VALUE_5, STR_VALUE_3, STR_VALUE_1 },
+        "Pointer ADD operation",
+        "Pointers \"/2\", \"/1\", \"/0\" ADD sequence failed on simple JSON array");
+    verifyAddValues(result, in, check, new String[] { "/0", "/2", "/4" },
+        new String[] { STR_VALUE_1, STR_VALUE_3, STR_VALUE_5 },
+        "Pointer ADD operation",
+        "Pointers \"/0\", \"/2\", \"/4\" ADD sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer ADD operation for {@code int} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectInt();
+    final JsonPointer ptr = Json.createPointer(INT_PATH);
+    final JsonObject out = ptr.add(in, Json.createValue(INT_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + INT_PATH + "\" ADD \""
+          + INT_VALUE + "\" failed on empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code int} on empty JSON array. Only
+   * allowed index for empty array is {@code 0} and {@code -}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnEmptyArray(final TestResult result) {
+    System.out.println(" - for int on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithInt();
+    final JsonPointer[] ptrs = new JsonPointer[] { Json.createPointer("/0"),
+        Json.createPointer("/-") };
+    for (final JsonPointer ptr : ptrs) {
+      final JsonArray out = ptr.add(in, Json.createValue(INT_VALUE));
+      if (!assertEquals(check, out)) {
+        result.fail("Pointer ADD operation", "Pointer \"" + ptr + "\" ADD \""
+            + INT_VALUE + "\" failed on empty JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithInt();
+    final JsonPointer ptr = Json.createPointer(INT_PATH);
+    final JsonObject out = ptr.add(in, Json.createValue(INT_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + INT_PATH + "\" ADD \""
+          + INT_VALUE + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code int} on simple JSON array of size 1.
+   * Using index {@code 0} to add {@code int} before already existing element
+   * and index {@code 1} and {@code -} to add {@code int} after already existing
+   * element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 1");
+    final JsonArray in = createIntArray1();
+    final JsonArray checkBefore = createSimpleIntArrayWithIntBefore();
+    final JsonArray checkAfter = createSimpleIntArrayWithIntAfter();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer[] ptrsAfter = new JsonPointer[] {
+        Json.createPointer("/1"), Json.createPointer("/-") };
+    final JsonArray outBefore = ptrBefore.add(in, Json.createValue(INT_VALUE));
+    if (!assertEquals(checkBefore, outBefore)) {
+      result.fail("Pointer ADD operation", "Pointer \"/0\" ADD \"" + INT_VALUE
+          + "\" failed on simple JSON array");
+    }
+    for (final JsonPointer ptrAfter : ptrsAfter) {
+      final JsonArray outAfter = ptrAfter.add(in, Json.createValue(INT_VALUE));
+      if (!assertEquals(checkAfter, outAfter)) {
+        result.fail("Pointer ADD operation", "Pointer \"/1\" ADD \"" + INT_VALUE
+            + "\" failed on simple JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code int} on simple JSON array of size 2.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code int} at the end, in the middle and at the beginning of
+   * this array.
+   * <li>Adding {@code int} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray in = createIntArray2();
+    final JsonArray check = createSimpleIntArray5();
+    verifyAddValues(result, in, check, new String[] { "/2", "/1", "/0" },
+        new Integer[] { INT_VALUE_5, INT_VALUE_3, INT_VALUE_1 },
+        "Pointer ADD operation",
+        "Pointers \"/2\", \"/1\", \"/0\" ADD sequence failed on simple JSON array");
+    verifyAddValues(result, in, check, new String[] { "/0", "/2", "/4" },
+        new Integer[] { INT_VALUE_1, INT_VALUE_3, INT_VALUE_5 },
+        "Pointer ADD operation",
+        "Pointers \"/0\", \"/2\", \"/4\" ADD sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer ADD operation for {@code boolean} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectBool();
+    final JsonPointer ptr = Json.createPointer(BOOL_PATH);
+    final JsonObject out = ptr.add(in, toJsonValue(BOOL_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + BOOL_PATH
+          + "\" ADD \"" + BOOL_VALUE + "\" failed on empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code boolean} on empty JSON array. Only
+   * allowed index for empty array is {@code 0} and {@code -}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBoolOnEmptyArray(final TestResult result) {
+    System.out.println(" - for boolean on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithBool();
+    final JsonPointer[] ptrs = new JsonPointer[] { Json.createPointer("/0"),
+        Json.createPointer("/-") };
+    for (final JsonPointer ptr : ptrs) {
+      final JsonArray out = ptr.add(in, toJsonValue(BOOL_VALUE));
+      if (!assertEquals(check, out)) {
+        result.fail("Pointer ADD operation", "Pointer \"" + ptr + "\" ADD \""
+            + BOOL_VALUE + "\" failed on empty JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObject();
+    final JsonObject check = createSimpleObjectWithBool();
+    final JsonPointer ptr = Json.createPointer(BOOL_PATH);
+    final JsonObject out = ptr.add(in, toJsonValue(BOOL_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + BOOL_PATH
+          + "\" ADD \"" + BOOL_VALUE + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code boolean} on simple JSON array of size
+   * 1. Using index {@code 0} to add {@code boolean} before already existing
+   * element and index {@code 1} and {@code -} to add {@code boolean} after
+   * already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 1");
+    final JsonArray in = createBoolArray1();
+    final JsonArray checkBefore = createSimpleBoolArrayWithBoolBefore();
+    final JsonArray checkAfter = createSimpleBoolArrayWithBoolAfter();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer[] ptrsAfter = new JsonPointer[] {
+        Json.createPointer("/1"), Json.createPointer("/-") };
+    final JsonArray outBefore = ptrBefore.add(in, toJsonValue(BOOL_FALSE));
+    if (!assertEquals(checkBefore, outBefore)) {
+      result.fail("Pointer ADD operation", "Pointer \"/0\" ADD \"" + BOOL_FALSE
+          + "\" failed on simple JSON array");
+    }
+    for (final JsonPointer ptrAfter : ptrsAfter) {
+      final JsonArray outAfter = ptrAfter.add(in, toJsonValue(BOOL_FALSE));
+      if (!assertEquals(checkAfter, outAfter)) {
+        result.fail("Pointer ADD operation", "Pointer \"/1\" ADD \""
+            + BOOL_FALSE + "\" failed on simple JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code boolean} on simple JSON array of size
+   * 2. Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code boolean} at the end, in the middle and at the beginning
+   * of this array.
+   * <li>Adding {@code boolean} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray in = createBoolArray2();
+    final JsonArray check = createSimpleBoolArray5();
+    verifyAddValues(result, in, check, new String[] { "/2", "/1", "/0" },
+        new Boolean[] { BOOL_TRUE, BOOL_TRUE, BOOL_FALSE },
+        "Pointer ADD operation",
+        "Pointers \"/2\", \"/1\", \"/0\" ADD sequence failed on simple JSON array");
+    verifyAddValues(result, in, check, new String[] { "/0", "/2", "/4" },
+        new Boolean[] { BOOL_FALSE, BOOL_TRUE, BOOL_TRUE },
+        "Pointer ADD operation",
+        "Pointers \"/0\", \"/2\", \"/4\" ADD sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer ADD operation for {@code JsonObject} on empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject on empty JSON object");
+    final JsonObject in = createEmptyObject();
+    final JsonObject check = createSimpleObjectObject();
+    final JsonPointer ptr = Json.createPointer(OBJ_PATH);
+    final JsonObject out = ptr.add(in, OBJ_VALUE);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + OBJ_PATH + "\" ADD \""
+          + OBJ_VALUE + "\" failed on empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code JsonObject} on empty JSON array. Only
+   * allowed index for empty array is {@code 0} and {@code -}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnEmptyArray(final TestResult result) {
+    System.out.println(" - for JsonObject on empty JSON array");
+    final JsonArray in = createEmptyArray();
+    final JsonArray check = createEmptyArrayWithObject();
+    final JsonPointer[] ptrs = new JsonPointer[] { Json.createPointer("/0"),
+        Json.createPointer("/-") };
+    for (final JsonPointer ptr : ptrs) {
+      final JsonArray out = ptr.add(in, OBJ_VALUE);
+      if (!assertEquals(check, out)) {
+        result.fail("Pointer ADD operation", "Pointer \"" + ptr + "\" ADD \""
+            + OBJ_VALUE + "\" failed on empty JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectWithObject();
+    final JsonPointer ptr = Json.createPointer(OBJ_PATH);
+    final JsonObject out = ptr.add(in, OBJ_VALUE);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + OBJ_PATH + "\" ADD \""
+          + OBJ_VALUE + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code JsonObject} on simple JSON array of
+   * size 1. Using index {@code 0} to add {@code JsonObject} before already
+   * existing element and index {@code 1} and {@code -} to add
+   * {@code JsonObject} after already existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 1");
+    final JsonArray in = createObjectArray1();
+    final JsonArray checkBefore = createSimpleObjectArrayWithObjectBefore();
+    final JsonArray checkAfter = createSimpleObjectArrayWithObjectAfter();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer[] ptrsAfter = new JsonPointer[] {
+        Json.createPointer("/1"), Json.createPointer("/-") };
+    final JsonArray outBefore = ptrBefore.add(in, OBJ_VALUE);
+    if (!assertEquals(checkBefore, outBefore)) {
+      result.fail("Pointer ADD operation", "Pointer \"/0\" ADD \"" + OBJ_VALUE
+          + "\" failed on simple JSON array");
+    }
+    for (final JsonPointer ptrAfter : ptrsAfter) {
+      final JsonArray outAfter = ptrAfter.add(in, OBJ_VALUE);
+      if (!assertEquals(checkAfter, outAfter)) {
+        result.fail("Pointer ADD operation", "Pointer \"/1\" ADD \"" + OBJ_VALUE
+            + "\" failed on simple JSON array");
+      }
+    }
+  }
+
+  /**
+   * Test pointer ADD operation for {@code JsonObject} on simple JSON array of
+   * size 2. Starting with an array of size 2.
+   * <ul>
+   * <li>Adding {@code JsonObject} at the end, in the middle and at the
+   * beginning of this array.
+   * <li>Adding {@code JsonObject} at the beginning, in the middle and at the
+   * end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testAddObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray in = createObjectArray2();
+    final JsonArray check = createSimpleObjectArray5();
+    verifyAddValues(result, in, check, new String[] { "/2", "/1", "/0" },
+        new JsonObject[] { OBJ_VALUE_5, OBJ_VALUE_3, OBJ_VALUE_1 },
+        "Pointer ADD operation",
+        "Pointers \"/2\", \"/1\", \"/0\" ADD sequence failed on simple JSON array");
+    verifyAddValues(result, in, check, new String[] { "/0", "/2", "/4" },
+        new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_3, OBJ_VALUE_5 },
+        "Pointer ADD operation",
+        "Pointers \"/0\", \"/2\", \"/4\" ADD sequence failed on simple JSON array");
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test that existing target object is replaced by specified array when ADD
+   * operation is applied.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>}:<br>
+   * When the operation is applied, the target location MUST reference one of:
+   * <ul>
+   * <li>A member to add to an existing object - whereupon the supplied value is
+   * added to that object at the indicated location. If the member already
+   * exists, it is replaced by the specified value.</li>
+   * <li>...</li>
+   * </ul>
+   */
+  private void testAddArrayToReplaceObject(final TestResult result) {
+    System.out.println(" - for JsonArray to replace JsonObject");
+    final JsonObject in = createCompoundObject();
+    final JsonObject check = createCompoundObjectWithObjectReplaced();
+    final JsonPointer ptr = Json.createPointer(DEF_OBJ_PATH);
+    final JsonArray replace = createSimpleStringArray5();
+    final JsonObject out = ptr.add(in, replace);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + DEF_OBJ_PATH
+          + "\" ADD array to replace existing object failed on compound JSON object");
+    }
+  }
+
+  /**
+   * Test that whole document is replaced by specified array when ADD operation
+   * is applied with root pointer.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>}:<br>
+   * When the operation is applied, the target location MUST reference one of:
+   * <ul>
+   * <li>The root of the target document - whereupon the specified value becomes
+   * the entire content of the target document.</li>
+   * <li>...</li>
+   * </ul>
+   */
+  private void testAddArrayToReplaceDocument(final TestResult result) {
+    System.out.println(" - for JsonArray to replace whole document");
+    final JsonObject in = createCompoundObject();
+    final JsonArray check = createSimpleStringArray5();
+    final JsonPointer ptr = Json.createPointer("");
+    final JsonArray replace = createSimpleStringArray5();
+    // Instance being replaced is JsonObject, instance being added is JsonArray
+    final JsonStructure out = ptr.add((JsonStructure) in, replace);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation", "Pointer \"" + DEF_OBJ_PATH
+          + "\" ADD array to replace existing object failed on compound JSON object");
+    }
+  }
+
+  /**
+   * Test ADD operation of an array of {@code String}s into existing array of
+   * {@code String}s. This scenario is inspired by
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>} operation example {@code { "op": "add", "path": "/a/b/c",
+   * "value": [ "foo", "bar" ] }} and following explanation of this operation on
+   * an array:
+   * <ul>
+   * <li>An element to add to an existing array - whereupon the supplied value
+   * is added to the array at the indicated location. Any elements at or above
+   * the specified index are shifted one position to the right. The specified
+   * index MUST NOT be greater than the number of elements in the array. If the
+   * "-" character is used to index the end of the array (see [RFC6901]), this
+   * has the effect of appending the value to the array.</li>
+   * </ul>
+   */
+  private void testAddStringArrayToStringArray(final TestResult result) {
+    System.out.println(" - for String array to be added to existing String array");
+    final JsonArray in = createStringArray2();
+    final JsonArray check = createStringArray2WithStringArrayInTheMiddle();
+    final JsonArray arrayToAdd = createStringInnerArray2();
+    final JsonPointer ptr = Json.createPointer("/1");
+    final JsonArray out = ptr.add(in, arrayToAdd);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer ADD operation",
+          "Pointer \"/1\" ADD array failed on JSON array");
+    }
+  }
+
+  /**
+   * Test ADD operation on non existing JsonObject. This scenario is described
+   * in {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC
+   * 6902: 4.1. add</a>} error handling samples. Test is trying to ADD value {
+   * "address" : "In a galaxy far far away"} into object { "name" : "John Smith"
+   * } using path "/child/address". Even "/child" path does not exist so this
+   * operation must fail.
+   * 
+   */
+  private void testAddStringToNonExistingObject(final TestResult result) {
+    System.out.println(" - for String to be added to non existing JsonObject");
+    final JsonObject in = createSimpleObject();
+    final JsonPointer ptr = Json.createPointer(DEF_OBJ_PATH + STR_PATH);
+    boolean exception = false;
+    try {
+      ptr.add(in, Json.createValue(STR_VALUE));
+    } catch (JsonException e) {
+      exception = true;
+      System.out.println("    - Expected exception: " + e.getMessage());
+    }
+    if (!exception) {
+      result.fail("Pointer ADD operation",
+          "ADD operation on non existing JsonObject \"" + DEF_OBJ_PATH
+              + "\" passed");
+    }
+  }
+
+  /**
+   * Test helper: Verify set of ADD operations on provided JSON array and verify
+   * result using provided expected JSON value. JSON pointer instance is used to
+   * modify the array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON array to be modified.
+   * @param check
+   *          Expected modified JSON array (used for operation check).
+   * @param paths
+   *          JSON array paths of values to be added. Pairs of {@code paths[i]}
+   *          and {@code values[i]} are used for add operations.
+   * @param values
+   *          JSON array values to be added on specified indexes.
+   * @param testName
+   *          Name of this test.
+   * @param errorMessage
+   *          Error message to be added on verification failure.
+   */
+  private void verifyAddValues(final TestResult result, final JsonArray in,
+      final JsonArray check, final String[] paths, final Object[] values,
+      final String testName, final String errorMessage) {
+    if (paths.length != values.length) {
+      throw new IllegalArgumentException(
+          "Number of paths does not match number of indexes");
+    }
+    JsonArray out = in;
+    for (int i = 0; i < paths.length; i++) {
+      final JsonPointer ptr = Json.createPointer(paths[i]);
+      out = ptr.add(out, toJsonValue(values[i]));
+    }
+    if (!assertEquals(check, out)) {
+      result.fail(testName, errorMessage);
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerRemove.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerRemove.java
new file mode 100644
index 0000000..3622a4e
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerRemove.java
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.pointertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: pointer
+ * usage for {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}
+ * remove operation tests.<br>
+ */
+public class PointerRemove {
+
+  /**
+   * Creates an instance of RFC 6901 pointer instance usage for RFC 6902 remove
+   * operation tests.
+   */
+  PointerRemove() {
+    super();
+  }
+
+  /**
+   * Test RFC 6901 pointer instance usage for RFC 6902 remove operation. Suite
+   * entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 6901 pointer usage for RFC 6902 remove operation");
+    System.out.println("Testing RFC 6901 pointer usage for RFC 6902 remove operation");
+    testRemoveStringOnEmptyObject(result);
+    testRemoveStringOnEmptyArray(result);
+    testRemoveStringOnSimpleObject(result);
+    testRemoveStringOnSimpleArray(result);
+    testRemoveStringOnSimpleArray2(result);
+    testRemoveIntOnEmptyObject(result);
+    testRemoveIntOnEmptyArray(result);
+    testRemoveIntOnSimpleObject(result);
+    testRemoveIntOnSimpleArray(result);
+    testRemoveIntOnSimpleArray2(result);
+    testRemoveBoolOnEmptyObject(result);
+    testRemoveBoolOnEmptyArray(result);
+    testRemoveBoolOnSimpleObject(result);
+    testRemoveBoolOnSimpleArray(result);
+    testRemoveBoolOnSimpleArray2(result);
+    testRemoveObjectOnEmptyObject(result);
+    testRemoveObjectOnEmptyArray(result);
+    testRemoveObjectOnSimpleObject(result);
+    testRemoveObjectOnSimpleArray(result);
+    testRemoveObjectOnSimpleArray2(result);
+    testRemoveFromNonExistingLocationInObject(result);
+    testRemoveFromNonExistingLocationInArray(result);
+    return result;
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code String} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnEmptyObject(final TestResult result) {
+    System.out.println(" - for String to produce empty JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createEmptyObject();
+    final JsonPointer ptr = Json.createPointer(STR_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation", "Pointer \"" + STR_PATH
+          + "\" REMOVE failed when producing empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code String} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnEmptyArray(final TestResult result) {
+    System.out.println(" - for String to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithStr();
+    final JsonArray check = createEmptyArray();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed when producing empty JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectWithStr();
+    final JsonObject check = createSimpleObject();
+    final JsonPointer ptr = Json.createPointer(STR_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"" + STR_PATH + "\" REMOVE failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code String} on simple JSON array of
+   * size 2. Using index {@code 0} to remove {@code String} before another
+   * existing element and index {@code 1} to remove {@code String} after another
+   * existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleStringArrayWithStrBefore();
+    final JsonArray inAfter = createSimpleStringArrayWithStrAfter();
+    final JsonArray check = createStringArray1();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer ptrAfter = Json.createPointer("/1");
+    final JsonArray outBefore = ptrBefore.remove(inBefore);
+    final JsonArray outAfter = ptrAfter.remove(inAfter);
+    if (!assertEquals(check, outBefore)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed on simple JSON array");
+    }
+    if (!assertEquals(check, outAfter)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/1\" REMOVE failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for {@code String} on simple JSON array of size 5.
+   * Starting with an array of size 2.
+   * <ul>
+   * <li>Removing {@code String} at the end, at the middle and at the beginning
+   * of this array.
+   * <li>Removing {@code String} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final JsonArray check = createStringArray2();
+    verifyRemoveValues(result, in, check, new String[] { "/4", "/2", "/0" },
+        "Pointer REMOVE operation",
+        "Pointers \"/4\", \"/2\", \"/0\" REMOVE sequence failed on simple JSON array");
+    verifyRemoveValues(result, in, check, new String[] { "/0", "/1", "/2" },
+        "Pointer REMOVE operation",
+        "Pointers \"/0\", \"/1\", \"/2\" REMOVE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} to produce empty JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnEmptyObject(final TestResult result) {
+    System.out.println(" - for int to produce empty JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createEmptyObject();
+    final JsonPointer ptr = Json.createPointer(INT_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation", "Pointer \"" + INT_PATH
+          + "\" REMOVE failed when producing empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} to produce empty JSON array.
+   * Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnEmptyArray(final TestResult result) {
+    System.out.println(" - for int to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithInt();
+    final JsonArray check = createEmptyArray();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed when producing empty JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectWithInt();
+    final JsonObject check = createSimpleObject();
+    final JsonPointer ptr = Json.createPointer(INT_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"" + INT_PATH + "\" REMOVE failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code int} on simple JSON array of size
+   * 2. Using index {@code 0} to remove {@code int} before another existing
+   * element and index {@code 1} to remove {@code int} after another existing
+   * element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleIntArrayWithIntBefore();
+    final JsonArray inAfter = createSimpleIntArrayWithIntAfter();
+    final JsonArray check = createIntArray1();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer ptrAfter = Json.createPointer("/1");
+    final JsonArray outBefore = ptrBefore.remove(inBefore);
+    final JsonArray outAfter = ptrAfter.remove(inAfter);
+    if (!assertEquals(check, outBefore)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed on simple JSON array");
+    }
+    if (!assertEquals(check, outAfter)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/1\" REMOVE failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for {@code int} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code int} at the end, at the middle and at the beginning of
+   * this array.
+   * <li>Removing {@code int} at the beginning, in the middle and at the end of
+   * this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final JsonArray check = createIntArray2();
+    verifyRemoveValues(result, in, check, new String[] { "/4", "/2", "/0" },
+        "Pointer REMOVE operation",
+        "Pointers \"/4\", \"/2\", \"/0\" REMOVE sequence failed on simple JSON array");
+    verifyRemoveValues(result, in, check, new String[] { "/0", "/1", "/2" },
+        "Pointer REMOVE operation",
+        "Pointers \"/0\", \"/1\", \"/2\" REMOVE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnEmptyObject(final TestResult result) {
+    System.out.println(" - for boolean to produce empty JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createEmptyObject();
+    final JsonPointer ptr = Json.createPointer(BOOL_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation", "Pointer \"" + BOOL_PATH
+          + "\" REMOVE failed when producing empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnEmptyArray(final TestResult result) {
+    System.out.println(" - for boolean to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithBool();
+    final JsonArray check = createEmptyArray();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed when producing empty JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectWithBool();
+    final JsonObject check = createSimpleObject();
+    final JsonPointer ptr = Json.createPointer(BOOL_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"" + BOOL_PATH + "\" REMOVE failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code boolean} on simple JSON array of
+   * size 2. Using index {@code 0} to remove {@code boolean} before another
+   * existing element and index {@code 1} to remove {@code boolean} after
+   * another existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleBoolArrayWithBoolBefore();
+    final JsonArray inAfter = createSimpleBoolArrayWithBoolAfter();
+    final JsonArray check = createBoolArray1();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer ptrAfter = Json.createPointer("/1");
+    final JsonArray outBefore = ptrBefore.remove(inBefore);
+    final JsonArray outAfter = ptrAfter.remove(inAfter);
+    if (!assertEquals(check, outBefore)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed on simple JSON array");
+    }
+    if (!assertEquals(check, outAfter)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/1\" REMOVE failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for {@code boolean} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code boolean} at the end, at the middle and at the beginning
+   * of this array.
+   * <li>Removing {@code boolean} at the beginning, in the middle and at the end
+   * of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 5");
+    final JsonArray in = createSimpleBoolArray5();
+    final JsonArray check = createBoolArray2();
+    verifyRemoveValues(result, in, check, new String[] { "/4", "/2", "/0" },
+        "Pointer REMOVE operation",
+        "Pointers \"/4\", \"/2\", \"/0\" REMOVE sequence failed on simple JSON array");
+    verifyRemoveValues(result, in, check, new String[] { "/0", "/1", "/2" },
+        "Pointer REMOVE operation",
+        "Pointers \"/0\", \"/1\", \"/2\" REMOVE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} to produce empty JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnEmptyObject(final TestResult result) {
+    System.out.println(" - for JsonObject to produce empty JSON object");
+    final JsonObject in = createSimpleObjectObject();
+    final JsonObject check = createEmptyObject();
+    final JsonPointer ptr = Json.createPointer(OBJ_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation", "Pointer \"" + OBJ_PATH
+          + "\" REMOVE failed when producing empty JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} to produce empty JSON
+   * array. Only allowed index for empty array is {@code 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnEmptyArray(final TestResult result) {
+    System.out.println(" - for JsonObject to produce empty JSON array");
+    final JsonArray in = createEmptyArrayWithObject();
+    final JsonArray check = createEmptyArray();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed when producing empty JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObjectWithObject();
+    final JsonObject check = createCompoundObject();
+    final JsonPointer ptr = Json.createPointer(OBJ_PATH);
+    final JsonObject out = ptr.remove(in);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"" + OBJ_PATH + "\" REMOVE failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE operation for {@code JsonObject} on simple JSON array
+   * of size 2. Using index {@code 0} to remove {@code JsonObject} before
+   * another existing element and index {@code 1} to remove {@code JsonObject}
+   * after another existing element.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 2");
+    final JsonArray inBefore = createSimpleObjectArrayWithObjectBefore();
+    final JsonArray inAfter = createSimpleObjectArrayWithObjectAfter();
+    final JsonArray check = createObjectArray1();
+    final JsonPointer ptrBefore = Json.createPointer("/0");
+    final JsonPointer ptrAfter = Json.createPointer("/1");
+    final JsonArray outBefore = ptrBefore.remove(inBefore);
+    final JsonArray outAfter = ptrAfter.remove(inAfter);
+    if (!assertEquals(check, outBefore)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/0\" REMOVE failed on simple JSON array");
+    }
+    if (!assertEquals(check, outAfter)) {
+      result.fail("Pointer REMOVE operation",
+          "Pointer \"/1\" REMOVE failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for {@code JsonObject} on simple JSON array of size 5.
+   * Starting with an array of size 5.
+   * <ul>
+   * <li>Removing {@code JsonObject} at the end, at the middle and at the
+   * beginning of this array.
+   * <li>Removing {@code JsonObject} at the beginning, in the middle and at the
+   * end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testRemoveObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final JsonArray check = createObjectArray2();
+    verifyRemoveValues(result, in, check, new String[] { "/4", "/2", "/0" },
+        "Pointer REMOVE operation",
+        "Pointers \"/4\", \"/2\", \"/0\" REMOVE sequence failed on simple JSON array");
+    verifyRemoveValues(result, in, check, new String[] { "/0", "/1", "/2" },
+        "Pointer REMOVE operation",
+        "Pointers \"/0\", \"/1\", \"/2\" REMOVE sequence failed on simple JSON array");
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test pointer REMOVE for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testRemoveFromNonExistingLocationInObject(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        final JsonPointer ptr = Json.createPointer(paths[j]);
+        try {
+          final JsonObject out = ptr.remove(objsIn[i]);
+          result.fail("Pointer REMOVE operation", "Pointer \"" + paths[j]
+              + "\" REMOVE succeeded on non existing location");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test pointer REMOVE for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testRemoveFromNonExistingLocationInArray(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2()
+
+    };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        final JsonPointer ptr = Json.createPointer(paths[j]);
+        try {
+          final JsonArray out = ptr.remove(arraysIn[i]);
+          result.fail("Pointer REMOVE operation", "Pointer \"" + paths[j]
+              + "\" REMOVE succeeded on non existing location");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test helper: Verify set of REMOVE operations on provided JSON array and
+   * verify result using provided expected JSON value. JSON pointer instance is
+   * used to modify the array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON array to be modified.
+   * @param check
+   *          Expected modified JSON array (used for operation check).
+   * @param paths
+   *          JSON array paths of values to be added. Pairs of {@code paths[i]}
+   *          and {@code values[i]} are used for add operations.
+   * @param testName
+   *          Name of this test.
+   * @param errorMessage
+   *          Error message to be added on verification failure.
+   */
+  private void verifyRemoveValues(final TestResult result, final JsonArray in,
+      final JsonArray check, final String[] paths, final String testName,
+      final String errorMessage) {
+    JsonArray out = in;
+    for (int i = 0; i < paths.length; i++) {
+      final JsonPointer ptr = Json.createPointer(paths[i]);
+      out = ptr.remove(out);
+    }
+    if (!assertEquals(check, out)) {
+      result.fail(testName, errorMessage);
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerReplace.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerReplace.java
new file mode 100644
index 0000000..0d9708d
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerReplace.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.pointertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: pointer
+ * usage for {@see <a href="https://tools.ietf.org/html/rfc6902">RFC 6902</a>}
+ * replace operation tests.<br>
+ */
+public class PointerReplace {
+
+  /**
+   * Creates an instance of RFC 6901 pointer instance usage for RFC 6902 replace
+   * operation tests.
+   */
+  PointerReplace() {
+    super();
+  }
+
+  /**
+   * Test RFC 6901 pointer instance usage for RFC 6902 replace operation. Suite
+   * entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult(
+        "RFC 6901 pointer usage for RFC 6902 replace operation");
+    System.out.println(
+        "Testing RFC 6901 pointer usage for RFC 6902 replace operation");
+    testReplaceStringOnSimpleObject(result);
+    testReplaceStringOnSimpleArray(result);
+    testReplaceStringOnSimpleArray2(result);
+    testReplaceIntOnSimpleObject(result);
+    testReplaceIntOnSimpleArray(result);
+    testReplaceIntOnSimpleArray2(result);
+    testReplaceBoolOnSimpleObject(result);
+    testReplaceBoolOnSimpleArray(result);
+    testReplaceBoolOnSimpleArray2(result);
+    testReplaceObjectOnCompoundObject(result);
+    testReplaceObjectOnSimpleArray(result);
+    testReplaceObjectOnSimpleArray2(result);
+    testReplaceOfNonExistingLocationInObject(result);
+    testReplaceOfNonExistingLocationInArray(result);
+    return result;
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code String} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleObject(final TestResult result) {
+    System.out.println(" - for String on simple JSON object");
+    final JsonObject in = createSimpleObjectStr();
+    final JsonObject check = createSimpleObjectReplaceStr();
+    final JsonPointer ptr = Json.createPointer(STR_PATH);
+    final JsonObject out = ptr.replace(in, Json.createValue(STR_VALUE2));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"" + STR_PATH
+          + "\" REPLACE \"" + STR_VALUE2 + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code String} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleArray(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 1");
+    final JsonArray in = createStringArray1();
+    final JsonArray check = createSimpleStringArrayReplaceStr();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.replace(in, Json.createValue(STR_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"/0\" REPLACE \""
+          + STR_VALUE + "\" failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code String} on simple JSON array of
+   * size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code String} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code String} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceStringOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for String on simple JSON array of size 5");
+    final JsonArray in = createSimpleStringArray5();
+    final JsonArray check = createSimpleStringArray5R();
+    verifyReplaceValues(result, in, check,
+        new String[] { "/4", "/3", "/1", "/0" },
+        new String[] { STR_VALUE_1, STR_VALUE_2, STR_VALUE_4, STR_VALUE_5 },
+        "Pointer REPLACE operation",
+        "Pointers \"/4\", \"/3\", \"/1\", \"/0\" REPLACE sequence failed on simple JSON array");
+    verifyReplaceValues(result, in, check,
+        new String[] { "/0", "/1", "/3", "/4" },
+        new String[] { STR_VALUE_5, STR_VALUE_4, STR_VALUE_2, STR_VALUE_1 },
+        "Pointer REPLACE operation",
+        "Pointers \"/0\", \"/1\", \"/3\", \"/4\" REPLACE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code int} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleObject(final TestResult result) {
+    System.out.println(" - for int on simple JSON object");
+    final JsonObject in = createSimpleObjectInt();
+    final JsonObject check = createSimpleObjectReplaceInt();
+    final JsonPointer ptr = Json.createPointer(INT_PATH);
+    final JsonObject out = ptr.replace(in, Json.createValue(INT_VALUE2));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"" + INT_PATH
+          + "\" REPLACE \"" + INT_VALUE2 + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code int} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleArray(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 1");
+    final JsonArray in = createIntArray1();
+    final JsonArray check = createSimpleIntArrayReplaceInt();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.replace(in, Json.createValue(INT_VALUE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"/0\" REPLACE \""
+          + INT_VALUE + "\" failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code int} on simple JSON array of size
+   * 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code int} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code int} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceIntOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for int on simple JSON array of size 5");
+    final JsonArray in = createSimpleIntArray5();
+    final JsonArray check = createSimpleIntArray5R();
+    verifyReplaceValues(result, in, check,
+        new String[] { "/4", "/3", "/1", "/0" },
+        new Integer[] { INT_VALUE_1, INT_VALUE_2, INT_VALUE_4, INT_VALUE_5 },
+        "Pointer REPLACE operation",
+        "Pointers \"/4\", \"/3\", \"/1\", \"/0\" REPLACE sequence failed on simple JSON array");
+    verifyReplaceValues(result, in, check,
+        new String[] { "/0", "/1", "/3", "/4" },
+        new Integer[] { INT_VALUE_5, INT_VALUE_4, INT_VALUE_2, INT_VALUE_1 },
+        "Pointer REPLACE operation",
+        "Pointers \"/0\", \"/1\", \"/3\", \"/4\" REPLACE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code boolean} on simple JSON object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleObject(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON object");
+    final JsonObject in = createSimpleObjectBool();
+    final JsonObject check = createSimpleObjectReplaceBool();
+    final JsonPointer ptr = Json.createPointer(BOOL_PATH);
+    final JsonObject out = ptr.replace(in, toJsonValue(BOOL_VALUE2));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"" + BOOL_PATH
+          + "\" REPLACE \"" + BOOL_VALUE2 + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code boolean} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleArray(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 1");
+    final JsonArray in = createBoolArray1();
+    final JsonArray check = createSimpleBoolArrayReplaceBool();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.replace(in, toJsonValue(BOOL_FALSE));
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"/0\" REPLACE \""
+          + BOOL_FALSE + "\" failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code boolean} on simple JSON array of
+   * size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code boolean} items from the end to the beginning of this
+   * array.
+   * <li>Replacing {@code boolean} from the beginning to the end of this array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceBoolOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for boolean on simple JSON array of size 5");
+    final JsonArray in = createSimpleBoolArray5();
+    final JsonArray check = createSimpleBoolArray5R();
+    verifyReplaceValues(result, in, check,
+        new String[] { "/4", "/3", "/1", "/0" },
+        new Boolean[] { BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, BOOL_TRUE },
+        "Pointer REPLACE operation",
+        "Pointers \"/4\", \"/3\", \"/1\", \"/0\" REPLACE sequence failed on simple JSON array");
+    verifyReplaceValues(result, in, check,
+        new String[] { "/0", "/1", "/3", "/4" },
+        new Boolean[] { BOOL_TRUE, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE },
+        "Pointer REPLACE operation",
+        "Pointers \"/0\", \"/1\", \"/3\", \"/4\" REPLACE sequence failed on simple JSON array");
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code JsonObject} on compound JSON
+   * object.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnCompoundObject(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON object");
+    final JsonObject in = createCompoundObjectWithObject();
+    final JsonObject check = createCompoundObjectReplaceObject();
+    final JsonPointer ptr = Json.createPointer(OBJ_PATH);
+    final JsonObject out = ptr.replace(in, OBJ_VALUE2);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"" + OBJ_PATH
+          + "\" REPLACE \"" + OBJ_VALUE2 + "\" failed on simple JSON object");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code JsonObject} on simple JSON array.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnSimpleArray(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 1");
+    final JsonArray in = createObjectArray1();
+    final JsonArray check = createSimpleObjectArrayReplaceObject();
+    final JsonPointer ptr = Json.createPointer("/0");
+    final JsonArray out = ptr.replace(in, OBJ_VALUE);
+    if (!assertEquals(check, out)) {
+      result.fail("Pointer REPLACE operation", "Pointer \"/0\" REPLACE \""
+          + OBJ_VALUE + "\" failed on simple JSON array");
+    }
+  }
+
+  /**
+   * Test pointer REPLACE operation for {@code JsonObject} on simple JSON array
+   * of size 5. Starting with an array of size 5.
+   * <ul>
+   * <li>Replacing {@code JsonObject} items from the end to the beginning of
+   * this array.
+   * <li>Replacing {@code JsonObject} from the beginning to the end of this
+   * array.
+   * </ul>
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testReplaceObjectOnSimpleArray2(final TestResult result) {
+    System.out.println(" - for JsonObject on simple JSON array of size 5");
+    final JsonArray in = createSimpleObjectArray5();
+    final JsonArray check = createSimpleObjectArray5R();
+    verifyReplaceValues(result, in, check,
+        new String[] { "/4", "/3", "/1", "/0" },
+        new JsonObject[] { OBJ_VALUE_1, OBJ_VALUE_2, OBJ_VALUE_4, OBJ_VALUE_5 },
+        "Pointer REPLACE operation",
+        "Pointers \"/4\", \"/3\", \"/1\", \"/0\" REPLACE sequence failed on simple JSON array");
+    verifyReplaceValues(result, in, check,
+        new String[] { "/0", "/1", "/3", "/4" },
+        new JsonObject[] { OBJ_VALUE_5, OBJ_VALUE_4, OBJ_VALUE_2, OBJ_VALUE_1 },
+        "Pointer REPLACE operation",
+        "Pointers \"/0\", \"/1\", \"/3\", \"/4\" REPLACE sequence failed on simple JSON array");
+  }
+
+  // Tests based on RFC 6902 definitions and examples.
+
+  /**
+   * Test pointer REPLACE for non existing location in object.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+   * 4.3. replace</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testReplaceOfNonExistingLocationInObject(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonObject");
+    final JsonObject[] objsIn = new JsonObject[] { createEmptyObject(),
+        createSimpleObject(), createCompoundObject() };
+    final String[] paths = new String[] { STR_PATH, INT_PATH, BOOL_PATH,
+        OBJ_PATH };
+    final JsonValue[] values = new JsonValue[] { Json.createValue(STR_VALUE),
+        Json.createValue(INT_VALUE), toJsonValue(BOOL_VALUE), OBJ_VALUE };
+    // Go trough all objects
+    for (int i = 0; i < objsIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        final JsonPointer ptr = Json.createPointer(paths[j]);
+        try {
+          final JsonObject out = ptr.replace(objsIn[i], values[i]);
+          result.fail("Pointer REPLACE operation", "Pointer \"" + paths[j]
+              + "\" REPLACE succeeded on non existing location");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test pointer REPLACE for non existing location in array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} defines:<br>
+   * The target location MUST exist for the operation to be successful.
+   */
+  private void testReplaceOfNonExistingLocationInArray(
+      final TestResult result) {
+    System.out.println(" - for non existing location in JsonArray");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray1(), createIntArray2(), createSimpleBoolArray5(),
+        createObjectArray2()
+
+    };
+    final String[] paths = new String[] { "/", "/-1", "/-", "/5", "/0a", "/42",
+        STR_PATH + "/0" };
+    final JsonValue[] values = new JsonValue[] { Json.createValue(STR_VALUE),
+        Json.createValue(STR_VALUE), Json.createValue(INT_VALUE),
+        toJsonValue(BOOL_VALUE), OBJ_VALUE };
+    // Go trough all arrays
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all paths
+      for (int j = 0; j < paths.length; j++) {
+        final JsonPointer ptr = Json.createPointer(paths[j]);
+        try {
+          final JsonArray out = ptr.replace(arraysIn[i], values[i]);
+          result.fail("Pointer REPLACE operation", "Pointer \"" + paths[j]
+              + "\" REPLACE succeeded on non existing location");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test helper: Verify set of REPLACE operations on provided JSON array and
+   * verify result using provided expected JSON value. JSON pointer instance is
+   * used to modify the array.
+   * 
+   * @param result
+   *          Test suite result.
+   * @param in
+   *          JSON array to be modified.
+   * @param check
+   *          Expected modified JSON array (used for operation check).
+   * @param paths
+   *          JSON array paths of values to be added. Pairs of {@code paths[i]}
+   *          and {@code values[i]} are used for add operations.
+   * @param values
+   *          JSON array values to be added on specified indexes.
+   * @param testName
+   *          Name of this test.
+   * @param errorMessage
+   *          Error message to be added on verification failure.
+   */
+  private void verifyReplaceValues(final TestResult result, final JsonArray in,
+      final JsonArray check, final String[] paths, final Object[] values,
+      final String testName, final String errorMessage) {
+    if (paths.length != values.length) {
+      throw new IllegalArgumentException(
+          "Number of paths does not match number of indexes");
+    }
+    JsonArray out = in;
+    for (int i = 0; i < paths.length; i++) {
+      final JsonPointer ptr = Json.createPointer(paths[i]);
+      out = ptr.replace(out, toJsonValue(values[i]));
+    }
+    if (!assertEquals(check, out)) {
+      result.fail(testName, errorMessage);
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerResolve.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerResolve.java
new file mode 100644
index 0000000..130df0f
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerResolve.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.pointertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonException;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonPointer;
+import jakarta.json.JsonValue;
+
+import static jakarta.jsonp.tck.api.common.JsonAssert.*;
+import static jakarta.jsonp.tck.api.common.PointerRFCObject.*;
+import static jakarta.jsonp.tck.api.common.SimpleValues.*;
+
+// $Id$
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: JavaScript
+ * Object Notation (JSON) Pointer resolving tests.<br>
+ */
+public class PointerResolve {
+
+  /**
+   * Creates an instance of RFC 6901 JSON Pointer resolver tests.
+   */
+  PointerResolve() {
+    super();
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver tests. Suite entry point.
+   * 
+   * @return Result of all tests in this suite.
+   */
+  TestResult test() {
+    final TestResult result = new TestResult("RFC 6901 pointer resolving");
+    System.out.println("Testing RFC 6901 pointer resolving");
+    testResolveWholeDocument(result);
+    testResolveEmptyName(result);
+    testResolveSimpleArray(result);
+    testResolveSimpleArrayItems(result);
+    testResolvePathWithSlash(result);
+    testResolvePathWithEncodedSlash(result);
+    testResolvePathWithPercent(result);
+    testResolvePathWithCaret(result);
+    testResolvePathWithVerticalBar(result);
+    testResolvePathWithBackSlash(result);
+    testResolvePathWithDoubleQuotes(result);
+    testResolvePathWithSpace(result);
+    testResolvePathWithTilde(result);
+    testResolvePathWithEncodedTilde(result);
+    testResolvePathWithEncodedTildeOne(result);
+    testResolveValidNumericIndexInArray(result);
+    testResolveMemberAfterLastInArray(result);
+    testResolveNumericIndexWithLeadingZeroInArray(result);
+    testResolvenonNumericIndexInArray(result);
+    return result;
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for the whole document path.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveWholeDocument(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_KEY_WHOLE + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = in;
+    final JsonPointer ptr = Json.createPointer(RFC_KEY_WHOLE);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_KEY_WHOLE + "\"",
+            "GET operation failed for \"" + RFC_KEY_WHOLE + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_KEY_WHOLE + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "": 0}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveEmptyName(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR2 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL2);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR2);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR2 + "\"",
+            "GET operation failed for \"" + RFC_PTR2 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR2 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "foo": ["bar", "baz"]}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveSimpleArray(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR1 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = RFC_VAL1;
+    final JsonPointer ptr = Json.createPointer(RFC_PTR1);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR1 + "\"",
+            "GET operation failed for \"" + RFC_PTR1 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR1 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "foo": ["bar", "baz"]} array
+   * elements.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolveSimpleArrayItems(final TestResult result) {
+    final String[] itemPtrs = new String[] { RFC_PTR1_ITEM1, RFC_PTR1_ITEM2 };
+    final String[] itemVals = new String[] { RFC_VAL1_ITEM1, RFC_VAL1_ITEM2 };
+    final JsonObject in = createRFC6901Object();
+    for (int i = 0; i < itemPtrs.length; i++) {
+      System.out.println(" - resolving of \"" + itemPtrs[i] + "\" pointer");
+      final JsonValue check = Json.createValue(itemVals[i]);
+      final JsonPointer ptr = Json.createPointer(itemPtrs[i]);
+      try {
+        final JsonValue out = ptr.getValue(in);
+        if (!assertEquals(out, check)) {
+          result.fail("GET \"" + itemPtrs[i] + "\"",
+              "GET operation failed for \"" + itemPtrs[i] + "\" path");
+        }
+      } catch (JsonException e) {
+        result.fail("GET \"" + itemPtrs[i] + "\"",
+            "GET operation exception: " + e.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "a/b": 1}. Character
+   * {@code '/'} is encoded as {@code "~1"} string.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedSlash(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR3_ENC + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL3);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR3_ENC);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR3_ENC + "\"",
+            "GET operation failed for \"" + RFC_PTR3_ENC + "\" path");
+      }
+    } catch (JsonException e) {
+      System.out.println("    ! Exception: " + e.getMessage());
+      result.fail("GET \"" + RFC_PTR3_ENC + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "a/b": 1}. Character
+   * {@code '/'} is not encoded as {@code "~1"} string. This results in invalid
+   * {@code "/a/b"} path and resolving such path must throw an exception.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithSlash(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR3 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonPointer ptr = Json.createPointer(RFC_PTR3);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      result.fail("GET \"" + RFC_PTR3 + "\"",
+          "GET operation succeeded for \"" + RFC_PTR3 + "\" path");
+    } catch (JsonException e) {
+      System.out.println("    - Expected exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "c%d": 2}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithPercent(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR4 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL4);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR4);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR4 + "\"",
+            "GET operation failed for \"" + RFC_PTR4 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR4 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "e^f": 3}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithCaret(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR5 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL5);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR5);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR5 + "\"",
+            "GET operation failed for \"" + RFC_PTR5 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR5 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "g|h": 4}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithVerticalBar(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR6 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL6);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR6);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR6 + "\"",
+            "GET operation failed for \"" + RFC_PTR6 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR6 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "i\\j": 5}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithBackSlash(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR7 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL7);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR7);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR7 + "\"",
+            "GET operation failed for \"" + RFC_PTR7 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR7 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "k\"l": 6}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithDoubleQuotes(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR8 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL8);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR8);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR8 + "\"",
+            "GET operation failed for \"" + RFC_PTR8 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR8 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code " ": 7}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithSpace(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR9 + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL9);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR9);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR9 + "\"",
+            "GET operation failed for \"" + RFC_PTR9 + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR9 + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "m~n": 8} without encoding.
+   * Passing this test is not mandatory.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-3">RFC 6901: 3.
+   * Syntax</a>} defines JSON pointer grammar as:<br>
+   * {@code json-pointer    = *( "/" reference-token )}<br>
+   * {@code reference-token = *( unescaped / escaped )}<br>
+   * {@code unescaped       = %x00-2E / %x30-7D / %x7F-10FFFF}<br>
+   * {@code escaped         = "~" ( "0" / "1" )}<br>
+   * Characters {@code '/'} and {@code '~'} are excluded from {@code unescaped}.
+   * But having {@code '~'} outside escape sequence may be acceptable.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithTilde(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR10 + "\" pointer (optional)");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL10);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR10);
+    boolean noError = true;
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        noError = false;
+        System.out.println("    - Pointer \"" + RFC_KEY10
+            + "\" did not return expected value");
+      }
+    } catch (JsonException e) {
+      noError = false;
+      System.out.println("    - Expected exception: " + e.getMessage());
+    }
+    if (noError) {
+      System.out.println(
+          "    - Pointer resolving accepts '~' outside escape sequence");
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "m~n": 8}. Character
+   * {@code '~'} is encoded as {@code "~0"} string.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedTilde(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_KEY10_ENC + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL10);
+    final JsonPointer ptr = Json.createPointer(RFC_KEY10_ENC);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_KEY10_ENC + "\"",
+            "GET operation failed for \"" + RFC_KEY10_ENC + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_KEY10_ENC + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for {@code "o~1p": 9}. String
+   * {@code "~1"} is encoded as {@code "~01"} String. Proper encoded sequences
+   * transformation is described in
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:
+   * {@code "the string '~01' correctly becomes '~1' after transformation"}.
+   * 
+   * @param result
+   *          Tests result record.
+   */
+  private void testResolvePathWithEncodedTildeOne(final TestResult result) {
+    System.out.println(" - resolving of \"" + RFC_PTR11_ENC + "\" pointer");
+    final JsonObject in = createRFC6901Object();
+    final JsonValue check = Json.createValue(RFC_VAL11);
+    final JsonPointer ptr = Json.createPointer(RFC_PTR11_ENC);
+    try {
+      final JsonValue out = ptr.getValue(in);
+      if (!assertEquals(out, check)) {
+        result.fail("GET \"" + RFC_PTR11_ENC + "\"",
+            "GET operation failed for \"" + RFC_PTR11_ENC + "\" path");
+      }
+    } catch (JsonException e) {
+      result.fail("GET \"" + RFC_PTR11_ENC + "\"",
+          "GET operation exception: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for existing numeric indexes of an
+   * array. {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC
+   * 6901: 4. Evaluation</a>} chapter:<br>
+   * If the currently referenced value is a JSON array, the reference token MUST
+   * contain either:
+   * <ul>
+   * <li>characters comprised of digits (see ABNF below; note that leading zeros
+   * are not allowed) that represent an unsigned base-10 integer value, making
+   * the new referenced value the array element with the zero-based index
+   * identified by the token</li>
+   * </ul>
+   */
+  private void testResolveValidNumericIndexInArray(final TestResult result) {
+    System.out.println(
+        " - resolving of pointer containing existing numeric array index");
+    final JsonArray[] arraysIn = new JsonArray[] { createSimpleStringArray5(),
+        createSimpleIntArray5(), createSimpleBoolArray5(),
+        createSimpleObjectArray5() };
+    final JsonValue[] strings = new JsonValue[] { toJsonValue(STR_VALUE_1),
+        toJsonValue(STR_VALUE_2), toJsonValue(STR_VALUE_3),
+        toJsonValue(STR_VALUE_4), toJsonValue(STR_VALUE_5) };
+    final JsonValue[] ints = new JsonValue[] { toJsonValue(INT_VALUE_1),
+        toJsonValue(INT_VALUE_2), toJsonValue(INT_VALUE_3),
+        toJsonValue(INT_VALUE_4), toJsonValue(INT_VALUE_5) };
+    final JsonValue[] bools = new JsonValue[] { toJsonValue(BOOL_FALSE),
+        toJsonValue(BOOL_TRUE), toJsonValue(BOOL_TRUE), toJsonValue(BOOL_FALSE),
+        toJsonValue(BOOL_TRUE) };
+    final JsonValue[] objs = new JsonValue[] { OBJ_VALUE_1, OBJ_VALUE_2,
+        OBJ_VALUE_3, OBJ_VALUE_4, OBJ_VALUE_5 };
+    final JsonValue[][] checks = new JsonValue[][] { strings, ints, bools,
+        objs };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all valid indexes in arrays
+      for (int j = 0; j < 5; j++) {
+        final String path = "/" + Integer.toString(j);
+        final JsonPointer ptr = Json.createPointer(path);
+        final JsonValue out = ptr.getValue(arraysIn[i]);
+        if (!assertEquals(out, checks[i][j])) {
+          JsonValue.ValueType type = checks[i][j].getValueType();
+          String typeName = type == JsonValue.ValueType.TRUE
+              || type == JsonValue.ValueType.FALSE ? "boolean"
+                  : type.toString().toLowerCase();
+          result.fail("GET \"" + path + "\"", "GET operation failed for \""
+              + path + "\" path on " + typeName + " array");
+        }
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for character {@code '-'} marking the
+   * end of an array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:<br>
+   * If the currently referenced value is a JSON array, the reference token MUST
+   * contain either:
+   * <ul>
+   * <li>exactly the single character "-", making the new referenced value the
+   * (nonexistent) member after the last array element</li>
+   * </ul>
+   * Note that the use of the "-" character to index an array will always result
+   * in such an error condition because by definition it refers to a nonexistent
+   * array element. Thus, applications of JSON Pointer need to specify how that
+   * character is to be handled, if it is to be useful.
+   */
+  private void testResolveMemberAfterLastInArray(final TestResult result) {
+    System.out.println(" - resolving of array \"/-\" pointer");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray(), createSimpleIntArray5(), createBoolArray2(),
+        createSimpleObjectArray5() };
+    final String[] typeNames = new String[] { "empty", "String", "int",
+        "boolean", "JsonObject" };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      final JsonPointer ptr = Json.createPointer("/-");
+      try {
+        final JsonValue out = ptr.getValue(arraysIn[i]);
+        result.fail("GET \"/-\"", "GET operation succeeded for \"/-\" key");
+      } catch (JsonException e) {
+        System.out.println("    - Expected exception for \"/-\" path in "
+            + typeNames[i] + " array: " + e.getMessage());
+      }
+    }
+  }
+
+  // TODO: Consider whether passing this test is mandatory or optional.
+  /**
+   * Test RFC 6901 JSON Pointer resolver for existing index with leading
+   * {@code '0'} on array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:<br>
+   * {@code array-index = %x30 / ( %x31-39 *(%x30-39) )} grammar rule prohibits
+   * indexes with leading {@code '0'} except the case when index is exactly
+   * {@code "0"}. Exact case for {@code "0"} is being checked in other tests.
+   * This test checks illegal values with leading {@code '0'} followed by valid
+   * index numbers.
+   */
+  private void testResolveNumericIndexWithLeadingZeroInArray(
+      final TestResult result) {
+    System.out.println(
+        " - resolving of pointer containing numeric array index with leading '0' (optional)");
+    final JsonArray[] arraysIn = new JsonArray[] { createSimpleStringArray5(),
+        createSimpleIntArray5(), createSimpleBoolArray5(),
+        createSimpleObjectArray5() };
+    final String[] typeNames = new String[] { "String", "int", "boolean",
+        "JsonObject" };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      // Go trough all valid indexes in arrays
+      for (int j = 0; j < 5; j++) {
+        final String path = "/0" + Integer.toString(j);
+        final JsonPointer ptr = Json.createPointer(path);
+        try {
+          final JsonValue out = ptr.getValue(arraysIn[i]);
+          System.out.println("    ! GET operation succeeded for \"" + path
+              + "\" path on " + typeNames[i] + " array");
+          // result.fail("GET \""+path+"\"",
+          // "GET operation succeeded for \""+path+"\" key on "+typeNames[i]+"
+          // array");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+  /**
+   * Test RFC 6901 JSON Pointer resolver for invalid index containing non
+   * numeric characters on array.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} chapter:<br>
+   * {@code array-index = %x30 / ( %x31-39 *(%x30-39) )} grammar rule prohibits
+   * indexes with anything else than sequence of digits. Index {@code '-'} is
+   * being checked in another tests. The only exception is path for whole
+   * document ({@code ""}) which must return the whole array.
+   */
+  private void testResolvenonNumericIndexInArray(final TestResult result) {
+    System.out.println(" - resolving of pointer containing non numeric array index");
+    final JsonArray[] arraysIn = new JsonArray[] { createEmptyArray(),
+        createStringArray(), createSimpleIntArray5(), createBoolArray2(),
+        createSimpleObjectArray5() };
+    final String[] typeNames = new String[] { "empty", "String", "int",
+        "boolean", "JsonObject" };
+    final String wholeDocument = "";
+    final String[] paths = new String[] { "/", "/1a", "/b4", "/name" };
+    // Go trough all array types
+    for (int i = 0; i < arraysIn.length; i++) {
+      final JsonPointer wholeDocPtr = Json.createPointer(wholeDocument);
+      try {
+        final JsonValue wholeOut = wholeDocPtr.getValue(arraysIn[i]);
+        if (!assertEquals(wholeOut, arraysIn[i])) {
+          result.fail("GET \"" + wholeDocument + "\"",
+              "GET operation failed for \"" + wholeDocument + "\" path on "
+                  + typeNames[i] + " array");
+        }
+      } catch (JsonException e) {
+        result.fail("GET \"" + wholeDocument + "\"",
+            "GET operation failed for \"" + wholeDocument + "\" path on "
+                + typeNames[i] + " array: " + e.getMessage());
+      }
+      for (int j = 0; j < paths.length; j++) {
+        final JsonPointer ptr = Json.createPointer(paths[j]);
+        try {
+          final JsonValue out = ptr.getValue(arraysIn[i]);
+          result.fail("GET \"" + paths[j] + "\"",
+              "GET operation succeeded for \"" + paths[j] + "\" path on "
+                  + typeNames[i] + " array");
+        } catch (JsonException e) {
+          // There are too many combinations to log them.
+        }
+      }
+    }
+  }
+
+}
diff --git a/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerTests.java b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerTests.java
new file mode 100644
index 0000000..571fe47
--- /dev/null
+++ b/tck/tck-tests/src/main/java/jakarta/jsonp/tck/api/pointertests/PointerTests.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.jsonp.tck.api.pointertests;
+
+import jakarta.jsonp.tck.api.common.TestResult;
+import jakarta.jsonp.tck.lib.harness.Fault;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// $Id$
+/**
+ * {@see <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>}: JavaScript
+ * Object Notation (JSON) Pointer compatibility tests.<br>
+ * JSON-P API defines {@link jakarta.json.JsonPointer} interface to work with RFC
+ * 6901 JSON Pointer.
+ */
+@RunWith(Arquillian.class)
+public class PointerTests {
+
+    @Deployment
+    public static WebArchive createTestArchive() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addPackages(true, PointerTests.class.getPackage().getName());
+    }
+  /**
+   * Test JSON-P API response on pointer resolving.<br>
+   * Checks set of JSON pointers from sample object of RFC 6901.
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901: 4.
+   * Evaluation</a>} and
+   * {@see <a href="https://tools.ietf.org/html/rfc6901#section-5">RFC 6901: 5.
+   * JSON String Representation</a>}.
+   * 
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonPointerResolveTest
+   * @assertion_ids: JSONP:JAVADOC:643; JSONP:JAVADOC:582; JSONP:JAVADOC:583;
+   *                 JSONP:JAVADOC:584; JSONP:JAVADOC:661; JSONP:JAVADOC:662;
+   *                 JSONP:JAVADOC:663;
+   * @test_Strategy: Test API response on various JSON pointer values.
+   */
+  @Test
+  public void jsonPointerResolveTest() throws Fault {
+    PointerResolve resolveTest = new PointerResolve();
+    final TestResult result = resolveTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.1">RFC 6902:
+   * 4.1. add</a>} operation using RFC 6901 pointer instance.<br>
+   * Checks set of simple JSON values.<br>
+   * 
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonPointerAddOperationTest
+   * @assertion_ids: JSONP:JAVADOC:642; JSONP:JAVADOC:582; JSONP:JAVADOC:583;
+   *                 JSONP:JAVADOC:584; JSONP:JAVADOC:661; JSONP:JAVADOC:662;
+   *                 JSONP:JAVADOC:663;
+   * @test_Strategy: Test API response on various JSON pointer values.
+   */
+  @Test
+  public void jsonPointerAddOperationTest() throws Fault {
+    PointerAdd addTest = new PointerAdd();
+    final TestResult result = addTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.2">RFC 6902:
+   * 4.2. remove</a>} operation using RFC 6901 pointer instance.<br>
+   * Checks set of simple JSON values.<br>
+   * 
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonPointerRemoveOperationTest
+   * @assertion_ids: JSONP:JAVADOC:644; JSONP:JAVADOC:582; JSONP:JAVADOC:661;
+   * @test_Strategy: Test API response on various JSON pointer values.
+   */
+  @Test
+  public void jsonPointerRemoveOperationTest() throws Fault {
+    PointerRemove removeTest = new PointerRemove();
+    final TestResult result = removeTest.test();
+    result.eval();
+  }
+
+  /**
+   * Test JSON-P API response on
+   * {@see <a href="https://tools.ietf.org/html/rfc6902#section-4.3">RFC 6902:
+   * 4.3. replace</a>} operation using RFC 6901 pointer instance.<br>
+   * Checks set of simple JSON values.<br>
+   * 
+   * @throws Fault
+   *           when this test failed.
+   * 
+   * @testName: jsonPointerReplaceOperationTest
+   * @assertion_ids: JSONP:JAVADOC:645; JSONP:JAVADOC:582; JSONP:JAVADOC:583;
+   *                 JSONP:JAVADOC:584; JSONP:JAVADOC:661; JSONP:JAVADOC:662;
+   *                 JSONP:JAVADOC:663;
+   * @test_Strategy: Test API response on various JSON pointer values.
+   */
+  @Test
+  public void jsonPointerReplaceOperationTest() throws Fault {
+    PointerReplace replaceTest = new PointerReplace();
+    final TestResult result = replaceTest.test();
+    result.eval();
+  }
+
+}
diff --git a/tck/tck-tests/src/main/resources/jsonArrayUTF16BE.json b/tck/tck-tests/src/main/resources/jsonArrayUTF16BE.json
new file mode 100644
index 0000000..69c0e86
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayUTF16BE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonArrayUTF8.json b/tck/tck-tests/src/main/resources/jsonArrayUTF8.json
new file mode 100644
index 0000000..3736875
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayUTF8.json
@@ -0,0 +1 @@
+["a旨䔬讞斉屗列z"]
\ No newline at end of file
diff --git a/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfData.json b/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfData.json
new file mode 100644
index 0000000..b7e3a59
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfData.json
@@ -0,0 +1,36 @@
+[
+	"",
+	[],
+	{},
+	"string",
+	100,
+	true,
+	false,
+	null,
+	{
+		"emptyString" : "",
+		"emptyArray" : [],
+		"emptyObject" : {},
+		"string" : "string",
+		"number" :  100,
+		"true" : true,
+		"false" : false,
+		"null" : null,
+		"object" : { "name" : "value" },
+		"array" : [ "one", "two" ]
+	},
+	[ "string", 100, true, false, null, { "name" : "value" }, [ "one", "two" ] ],
+	100,
+	-100,
+	9223372036854775807,
+	-9223372036854775808,
+	0.5,
+	-0.5,
+	7e3,
+	7e+3,
+	9E3,
+	9E+3,
+	7e-3,
+	7E-3,
+	"!@#$%^&*()_+|~1234567890-=;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+]
diff --git a/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfDataUTF16BE.json b/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfDataUTF16BE.json
new file mode 100644
index 0000000..8263796
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayWithAllTypesOfDataUTF16BE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonArrayWithEscapeCharsData.json b/tck/tck-tests/src/main/resources/jsonArrayWithEscapeCharsData.json
new file mode 100644
index 0000000..4f2084d
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayWithEscapeCharsData.json
@@ -0,0 +1,3 @@
+[
+	"popeye\"\\\/\b\f\n\r\tolive"
+]
diff --git a/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedArraysData.json b/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedArraysData.json
new file mode 100644
index 0000000..9ef74ba
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedArraysData.json
@@ -0,0 +1,92 @@
+[
+ [
+  "name1","value1",
+  "nested2",[
+   "name2","value2",
+   "nested3",[
+    "name3","value3",
+    "nested4",[
+     "name4","value4",
+     "nested5",[
+      "name5","value5",
+      "nested6",[
+       "name6","value6",
+       "nested7",[
+        "name7","value7",
+        "nested8",[
+         "name8","value8",
+         "nested9",[
+          "name9","value9",
+          "nested10",[
+           "name10","value10",
+           "nested11",[
+            "name11","value11",
+            "nested12",[
+             "name12","value12",
+             "nested13",[
+              "name13","value13",
+              "nested14",[
+               "name14","value14",
+               "nested15",[
+                "name15","value15",
+                "nested16",[
+                 "name16","value16",
+                 "nested17",[
+                  "name17","value17",
+                  "nested18",[
+                   "name18","value18",
+                   "nested19",[
+                    "name19","value19",
+                    "nested20",[
+                     "name20","value20",
+                     "nested21",[
+                      "name21","value21",
+                      "nested22",[
+                       "name22","value22",
+                       "nested23",[
+                        "name23","value23",
+                        "nested24",[
+                         "name24","value24",
+                         "nested25",[
+                          "name25","value25",
+                          "nested26",[
+                           "name26","value26",
+                           "nested27",[
+                            "name27","value27",
+                            "nested28",[
+                             "name28","value28",
+                             "nested29",[
+                              "name29","value29",
+                              "nested30",[
+                               "name30","value30"
+                              ]
+                             ]
+                            ]
+                           ]
+                          ]
+                         ]
+                        ]
+                       ]
+                      ]
+                     ]
+                    ]
+                   ]
+                  ]
+                 ]
+                ]
+               ]
+              ]
+             ]
+            ]
+           ]
+          ]
+         ]
+        ]
+       ]
+      ]
+     ]
+    ]
+   ]
+  ]
+ ]
+]
diff --git a/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedObjectsData.json b/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedObjectsData.json
new file mode 100644
index 0000000..f97781f
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonArrayWithLotsOfNestedObjectsData.json
@@ -0,0 +1,92 @@
+[
+ {
+  "name1" : "value1",
+  "nested2" : {
+   "name2" : "value2",
+   "nested3" : {
+    "name3" : "value3",
+    "nested4" : {
+     "name4" : "value4",
+     "nested5" : {
+      "name5" : "value5",
+      "nested6" : {
+       "name6" : "value6",
+       "nested7" : {
+        "name7" : "value7",
+        "nested8" : {
+         "name8" : "value8",
+         "nested9" : {
+          "name9" : "value9",
+          "nested10" : {
+           "name10" : "value10",
+           "nested11" : {
+            "name11" : "value11",
+            "nested12" : {
+             "name12" : "value12",
+             "nested13" : {
+              "name13" : "value13",
+              "nested14" : {
+               "name14" : "value14",
+               "nested15" : {
+                "name15" : "value15",
+                "nested16" : {
+                 "name16" : "value16",
+                 "nested17" : {
+                  "name17" : "value17",
+                  "nested18" : {
+                   "name18" : "value18",
+                   "nested19" : {
+                    "name19" : "value19",
+                    "nested20" : {
+                     "name20" : "value20",
+                     "nested21" : {
+                      "name21" : "value21",
+                      "nested22" : {
+                       "name22" : "value22",
+                       "nested23" : {
+                        "name23" : "value23",
+                        "nested24" : {
+                         "name24" : "value24",
+                         "nested25" : {
+                          "name25" : "value25",
+                          "nested26" : {
+                           "name26" : "value26",
+                           "nested27" : {
+                            "name27" : "value27",
+                            "nested28" : {
+                             "name28" : "value28",
+                             "nested29" : {
+                              "name29" : "value29",
+                              "nested30" : {
+                               "name30" : "value30"
+                              }
+                             }
+                            }
+                           }
+                          }
+                         }
+                        }
+                       }
+                      }
+                     }
+                    }
+                   }
+                  }
+                 }
+                }
+               }
+              }
+             }
+            }
+           }
+          }
+         }
+        }
+       }
+      }
+     }
+    }
+   }
+  }
+ }
+]
diff --git a/tck/tck-tests/src/main/resources/jsonHelloWorld.json b/tck/tck-tests/src/main/resources/jsonHelloWorld.json
new file mode 100644
index 0000000..9049ffd
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonHelloWorld.json
@@ -0,0 +1,4 @@
+{
+	"greetingObj":{"hello":"world"},
+	"greetingArr":["hello","world"]
+}
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16.json
new file mode 100644
index 0000000..fe2f539
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16BE.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16BE.json
new file mode 100644
index 0000000..d1ddcf5
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16BE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16LE.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16LE.json
new file mode 100644
index 0000000..c48ec6f
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF16LE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32BE.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32BE.json
new file mode 100644
index 0000000..cc6e10e
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32BE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32LE.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32LE.json
new file mode 100644
index 0000000..92c853c
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF32LE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF8.json b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF8.json
new file mode 100644
index 0000000..ef3478f
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectEncodingUTF8.json
@@ -0,0 +1 @@
+{"stringName":"stringValue","objectName":{"foo":"bar"},"arrayName":[1,2,3]}
diff --git a/tck/tck-tests/src/main/resources/jsonObjectUTF16LE.json b/tck/tck-tests/src/main/resources/jsonObjectUTF16LE.json
new file mode 100644
index 0000000..9fb29a8
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectUTF16LE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectUTF8.json b/tck/tck-tests/src/main/resources/jsonObjectUTF8.json
new file mode 100644
index 0000000..e40bd76
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectUTF8.json
@@ -0,0 +1 @@
+{"unicodeChars":"a旨䔬讞斉屗列z"}
\ No newline at end of file
diff --git a/tck/tck-tests/src/main/resources/jsonObjectUnknownEncoding.json b/tck/tck-tests/src/main/resources/jsonObjectUnknownEncoding.json
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectUnknownEncoding.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfData.json b/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfData.json
new file mode 100644
index 0000000..1e92477
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfData.json
@@ -0,0 +1,36 @@
+{
+	"emptyString" : "",
+	"emptyArray" : [],
+	"emptyObject" : {},
+	"string" : "string",
+	"number" : 100,
+	"true" : true,
+	"false" : false,
+	"null" : null,
+	"object" : {
+		"emptyString" : "",
+		"emptyArray" : [],
+		"emptyObject" : {},
+		"string" : "string",
+		"number" :  100,
+		"true" : true,
+		"false" : false,
+		"null" : null,
+		"object" : { "name" : "value" },
+		"array" : [ "one", "two" ]
+	},
+	"array" : [ "string", 100, true, false, null, { "name" : "value" }, [ "one", "two" ] ],
+	"intPositive" : 100,
+	"intNegative" : -100,
+	"longMax"     : 9223372036854775807,
+	"longMin"     : -9223372036854775808,
+	"fracPositive" : 0.5,
+	"fracNegative" : -0.5,
+	"expPositive1" : 7e3,
+	"expPositive2" : 7e+3,
+	"expPositive3" : 9E3,
+	"expPositive4" : 9E+3,
+	"expNegative1" : 7e-3,
+	"expNegative2" : 7E-3,
+	"asciiChars" : "!@#$%^&*()_+|~1234567890-=;',./<>? qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
+}
diff --git a/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfDataUTF16LE.json b/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfDataUTF16LE.json
new file mode 100644
index 0000000..a2c1b53
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectWithAllTypesOfDataUTF16LE.json
Binary files differ
diff --git a/tck/tck-tests/src/main/resources/jsonObjectWithEscapeCharsData.json b/tck/tck-tests/src/main/resources/jsonObjectWithEscapeCharsData.json
new file mode 100644
index 0000000..b043d7b
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectWithEscapeCharsData.json
@@ -0,0 +1,3 @@
+{
+	"escapeChars" : "popeye\"\\\/\b\f\n\r\tolive"
+}
diff --git a/tck/tck-tests/src/main/resources/jsonObjectWithLotsOfNestedObjectsData.json b/tck/tck-tests/src/main/resources/jsonObjectWithLotsOfNestedObjectsData.json
new file mode 100644
index 0000000..3ac34d6
--- /dev/null
+++ b/tck/tck-tests/src/main/resources/jsonObjectWithLotsOfNestedObjectsData.json
@@ -0,0 +1,92 @@
+{
+ "nested1" : {
+  "name1" : "value1",
+  "nested2" : {
+   "name2" : "value2",
+   "nested3" : {
+    "name3" : "value3",
+    "nested4" : {
+     "name4" : "value4",
+     "nested5" : {
+      "name5" : "value5",
+      "nested6" : {
+       "name6" : "value6",
+       "nested7" : {
+        "name7" : "value7",
+        "nested8" : {
+         "name8" : "value8",
+         "nested9" : {
+          "name9" : "value9",
+          "nested10" : {
+           "name10" : "value10",
+           "nested11" : {
+            "name11" : "value11",
+            "nested12" : {
+             "name12" : "value12",
+             "nested13" : {
+              "name13" : "value13",
+              "nested14" : {
+               "name14" : "value14",
+               "nested15" : {
+                "name15" : "value15",
+                "nested16" : {
+                 "name16" : "value16",
+                 "nested17" : {
+                  "name17" : "value17",
+                  "nested18" : {
+                   "name18" : "value18",
+                   "nested19" : {
+                    "name19" : "value19",
+                    "nested20" : {
+                     "name20" : "value20",
+                     "nested21" : {
+                      "name21" : "value21",
+                      "nested22" : {
+                       "name22" : "value22",
+                       "nested23" : {
+                        "name23" : "value23",
+                        "nested24" : {
+                         "name24" : "value24",
+                         "nested25" : {
+                          "name25" : "value25",
+                          "nested26" : {
+                           "name26" : "value26",
+                           "nested27" : {
+                            "name27" : "value27",
+                            "nested28" : {
+                             "name28" : "value28",
+                             "nested29" : {
+                              "name29" : "value29",
+                              "nested30" : {
+                               "name30" : "value30"
+                              }
+                             }
+                            }
+                           }
+                          }
+                         }
+                        }
+                       }
+                      }
+                     }
+                    }
+                   }
+                  }
+                 }
+                }
+               }
+              }
+             }
+            }
+           }
+          }
+         }
+        }
+       }
+      }
+     }
+    }
+   }
+  }
+ }
+}